////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxcaecstatsprocessor.cpp
/// @brief The class that implements IStatsProcessor for AEC.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxcaecstatsprocessor.h"
#include "camxcsljumptable.h"
#include "camxhal3metadatautil.h"
#include "camxhal3module.h"
#include "camxmem.h"
#include "camxtrace.h"
#include "camximagesensormoduledata.h"
#include "camximagesensordata.h"
#include "camxpropertyblob.h"
#include "camxtitan17xdefs.h"

CAMX_NAMESPACE_BEGIN

/// @todo (CAMX-1259): Adding IFE HW bit width in metadata. Query IFE HW bit width from metadata instead of hardcoding.
static const UINT8  BGStatsMaximumBitWidth      = 14;      ///< Bandwidth consumed by BG stats
static const FLOAT  MaxROIWeight                = 1000.0f; ///< Maximum ROI weight
static const UINT8  CompensationStepNumerator   = 1;       ///< EV compensation Step Numerator
static const UINT8  CompensationStepDenominator = 6;       ///< EV compensation Step denominator
class TuningDataManager;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::Create(
    IStatsProcessor** ppAECStatsProcessor)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    CamxResult          result              = CamxResultSuccess;
    CAECStatsProcessor* pAECStatsProcessor  = NULL;

    if (NULL == ppAECStatsProcessor)
    {
        CAMX_LOG_ERROR(CamxLogGroupAEC, "Invalid arguments");
        result = CamxResultEInvalidArg;
    }

    if (CamxResultSuccess == result)
    {
        pAECStatsProcessor = CAMX_NEW CAECStatsProcessor();
        if (NULL == pAECStatsProcessor)
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Create failed - out of memory");
            result = CamxResultENoMemory;
        }
        else
        {
            *ppAECStatsProcessor = pAECStatsProcessor;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::CAECStatsProcessor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAECStatsProcessor::CAECStatsProcessor()
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::Initialize(
    const StatsInitializeData* pInitializeData)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
    CamxResult result = CamxResultSuccess;

    if (NULL == pInitializeData)
    {
        return CamxResultEInvalidPointer;
    }

    m_pNode            = pInitializeData->pNode;
    m_pStatsParser     = pInitializeData->pHwContext->GetStatsParser();
    m_pDebugDataPool   = pInitializeData->pDebugDataPool;
    m_lastSettledState = AECSettleUnknown;
    m_cameraId         = pInitializeData->pPipeline->GetCameraId();

    if (NULL != pInitializeData->initializecallback.pAECCallback)
    {
        m_pfnCreate = pInitializeData->initializecallback.pAECCallback->pfnCreate;
    }

    const StaticSettings* pStaticSettings = pInitializeData->pHwContext->GetStaticSettings();
    CAMX_ASSERT(NULL != pStaticSettings);

    m_algoInput.statsSession.Initialize(pInitializeData);

    m_algoInput.cameraInfo = pInitializeData->cameraInfo;

    if (StatsAlgoRoleDefault != m_algoInput.cameraInfo.algoRole)
    {
        pInitializeData->pPipeline->GetIntraRealtimePipelineId(pInitializeData->pPipeline->GetPipelineId(),
            &m_peerpipelineId);
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Dual Camera Peer Pipeline:%d", m_peerpipelineId);
    }

    // If AEC stats processing is disabled we publish some hardcoded values to the metadata pool
    if (TRUE == pStaticSettings->disableAECStatsProcessing)
    {
        result = CamxResultSuccess;
    }
    else
    {
        // Create an instance of the core algorithm
        m_pAECEngine = CAECEngine::Create(m_pfnCreate, &m_algoInput.statsSession, &m_algoInput.cameraInfo);

        if (NULL == m_pAECEngine)
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Failed to create the AEC Engine - out of memory");
            result = CamxResultEUnableToLoad;
        }

        if (CamxResultSuccess == result)
        {
            // Start the driver
            AECCommandInputParam  inputParam  = { 0 };
            AECCommandOutputParam outputParam = { 0 };
            result = m_pAECEngine->HandleCommand(AECCommand::StartDriver, &inputParam, &outputParam);
        }

        if (CamxResultSuccess == result)
        {
            // Set the chromatix to driver
            AECCommandInputParam  inputParam  = { 0 };
            AECCommandOutputParam outputParam = { 0 };

            m_tuningData.pTuningSetManager = static_cast<VOID*>(pInitializeData->pTuningDataManager->GetChromatix());
            CAMX_ASSERT(m_tuningData.pTuningSetManager != NULL);

            ChiTuningMode tuningSelectors[1];
            tuningSelectors[0].mode                  = ChiModeType::Default;
            tuningSelectors[0].subMode               = { 0 };

            m_tuningData.pTuningModeSelectors = &tuningSelectors[0];
            m_tuningData.numSelectors         = 1;
            inputParam.pTuningData            = &m_tuningData;
            result = m_pAECEngine->HandleCommand(AECCommand::SetChromatix, &inputParam, &outputParam);
            m_tuningData.pTuningModeSelectors = NULL; /// Removing Local address
        }

        if (CamxResultSuccess == result)
        {
            AECCommandInputParam  inputParam  = { 0 };
            AECCommandOutputParam outputParam = { 0 };

            inputParam.systemLatency = static_cast<UINT8>(pStaticSettings->maxPipelineDelay);
            result                   = m_pAECEngine->HandleCommand(AECCommand::SetPipelineDelay, &inputParam, &outputParam);
        }

        if (CamxResultSuccess == result)
        {
            HwCameraInfo cameraInfo;

            // Retrieve the static capabilities for this camera
            result = HwEnvironment::GetInstance()->GetCameraInfo(pInitializeData->pPipeline->GetCameraId(), &cameraInfo);

            if (TRUE == cameraInfo.pSensorCaps->OTPData.dualCameraCalibration.isAvailable)
            {
                AECCommandInputParam  inputParam  = { 0 };
                AECCommandOutputParam outputParam = { 0 };
                const DualCameraSystemCalibrationdata* pSystemCalibrationData =
                    &cameraInfo.pSensorCaps->OTPData.dualCameraCalibration.systemCalibrationData;
                if (NULL != pSystemCalibrationData)
                {
                    inputParam.dcCalibrationInfo.version         = pSystemCalibrationData->calibrationFormatVersion;
                    inputParam.dcCalibrationInfo.brightnessRatio = pSystemCalibrationData->brightnessRatio;
                    inputParam.dcCalibrationInfo.slaveGain       = pSystemCalibrationData->referenceSlaveGain;
                    inputParam.dcCalibrationInfo.slaveTimeUnit   = pSystemCalibrationData->referenceSlaveExpTime;
                    inputParam.dcCalibrationInfo.masterGain      = pSystemCalibrationData->referenceMasterGain;
                    inputParam.dcCalibrationInfo.masterTimeUnit  = pSystemCalibrationData->referenceMasterExpTime;
                    inputParam.dcCalibrationInfo.CCT             = pSystemCalibrationData->referenceMasterColorTemperature;
                    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Multicamera:CalibData: Ver:%d BrtRatio:%f slave[G:%f ET:%f] "
                                     "master[G:%f ET:%f] cct:%d",
                                     inputParam.dcCalibrationInfo.version,
                                     inputParam.dcCalibrationInfo.brightnessRatio,
                                     inputParam.dcCalibrationInfo.slaveGain,
                                     inputParam.dcCalibrationInfo.slaveTimeUnit,
                                     inputParam.dcCalibrationInfo.masterGain,
                                     inputParam.dcCalibrationInfo.masterTimeUnit,
                                     inputParam.dcCalibrationInfo.CCT);

                    result = m_pAECEngine->HandleCommand(AECCommand::SetDCCalibrationData, &inputParam, &outputParam);
                }
                else
                {
                    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Multicamera :systemCalibrationData is not available");
                }

            }
            else
            {
                CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Multicamera :CalibData is not available");
            }
        }

        if (CamxResultSuccess == result)
        {
            AECCommandInputParam    inputParam  = { 0 };
            AECCommandOutputParam   outputParam = { 0 };
            StatsRectangle          cropWindow  = { 0 };

            GetCropWindow(&cropWindow);
            inputParam.pCropWindow = &cropWindow;

            result = m_pAECEngine->HandleCommand(AECCommand::ProcessCropWindow, &inputParam, &outputParam);
        }

        if (CamxResultSuccess == result)
        {
            AECCommandInputParam    inputParam   = { 0 };
            AECCommandOutputParam   outputParam  = { 0 };

            result = GetHardwareInfo(
                        &m_hardwareInfo,
                        pInitializeData->pHwContext,
                        pInitializeData->pPipeline->GetCameraId());
            if (CamxResultSuccess == result)
            {
                m_hardwareInfo.isDual = pInitializeData->pPipeline->IsDualCamera();
                inputParam.pHardwareInfo = &m_hardwareInfo;
                result = m_pAECEngine->HandleCommand(AECCommand::ProcessHardwareInfo, &inputParam, &outputParam);
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupAEC, "Failed to query the stats hardware information!");
            }
        }

        if (CamxResultSuccess == result)
        {
            result = VendorTagListAllocation();
        }

        if (CamxResultSuccess == result)
        {
            result = PublishPreRequestOutput(pInitializeData->pPipeline->GetPerFramePool(PoolType::PerUsecase));
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::ExecuteProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::ExecuteProcessRequest(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo)
{
    CAMX_ENTRYEXIT_SCOPE_ID(CamxLogGroupAEC, SCOPEEventCAECStatsProcessorExecuteProcessRequest,
        pStatsProcessRequestDataInfo->requestId);

    CamxResult                  result          = CamxResultSuccess;
    AECCommandInputParam        inputParam      = { 0 };
    AECEngineProcessStatsOutput engineOutput    = {};
    const StaticSettings*       pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();

    if (m_currProcessingRequestId != pStatsProcessRequestDataInfo->requestId-1)
    {
        CAMX_LOG_ERROR(CamxLogGroupAEC, "AEC: Execute Process Request: Id=%llu LastProcessed Id = %llu",
                       pStatsProcessRequestDataInfo->requestId, m_currProcessingRequestId);
    }
    else
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "AEC: Execute Process Request: Id=%llu LastProcessed Id = %llu",
                         pStatsProcessRequestDataInfo->requestId, m_currProcessingRequestId);
    }

    m_currProcessingRequestId = pStatsProcessRequestDataInfo->requestId;
    m_skipProcessing = pStatsProcessRequestDataInfo->skipProcessing;

    if (TRUE == m_skipProcessing)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC,
                         "Skipping AEC Algo processing for RequestId=%llu",
                         pStatsProcessRequestDataInfo->requestId);
    }
    else
    {
        // If AEC stats processing is disabled we publish some hardcoded values to the metadata pool
        if (TRUE == pStaticSettings->disableAECStatsProcessing)
        {
            result = OverwriteAECOutput(&engineOutput);
            m_outputParam.pProcessStatsOutput = &engineOutput;
        }
        else
        {
            CAMX_ASSERT((NULL != m_pAECEngine) && (NULL != pStatsProcessRequestDataInfo));

            GetPeerInfo(pStatsProcessRequestDataInfo, &m_HALParam.pPeerInfo);
            // Set the input parameters to the algorithm
            if (CamxResultSuccess == result)
            {
                // Set the HAL parameters
                result = ReadHALAECParam(&m_HALParam, pStatsProcessRequestDataInfo);
            }

            // Set the Tuning data to the algorithm
            if (CamxResultSuccess == result)
            {
                result = SetAlgoChromatix(pStatsProcessRequestDataInfo->pTuningModeData);
            }

            // Get LED Calibration config
            if ((CamxResultSuccess == result) && (DualLEDCalibrationDisabled != pStaticSettings->dualLEDCalibrationMode))
            {
                result = m_pAECEngine->HandleCommand(AECCommand::GetLEDCalibrationConfig, &inputParam, &m_outputParam);
            }

            // Load LED calibration data
            if ((CamxResultSuccess == result) && (DualLEDCalibrationDisabled == pStaticSettings->dualLEDCalibrationMode))
            {
                result = m_pAECEngine->HandleCommand(AECCommand::LoadLEDCalibrationData, &inputParam, &m_outputParam);
            }

            // Set the input parameters to the algorithm
            if (CamxResultSuccess == result)
            {
                result = SetAlgoSetParams(&m_HALParam);
            }

            if (CamxResultSuccess == result)
            {
                result = GetAlgoInput(pStatsProcessRequestDataInfo, &m_algoInput, &m_HALParam);
            }

            if (CamxResultSuccess == result)
            {
                result = PrepareAlgorithmOutput(&m_engineStatsOutput, pStatsProcessRequestDataInfo->requestId);
            }

            // Process the input
            if (CamxResultSuccess == result)
            {
                m_outputParam.pProcessStatsOutput = &m_engineStatsOutput;
                inputParam.pAlgorithmInput      = &m_algoInput;

                result = m_pAECEngine->HandleCommand(AECCommand::ProcessStats, &inputParam, &m_outputParam);
                // Publish cross pipeline property for multi camera
                PublishCrossProperty(pStatsProcessRequestDataInfo);
            }
        }

        if (CamxResultSuccess == result)
        {
            if (TRUE == pStatsProcessRequestDataInfo->reportConverged)
            {
                if (ControlAELockOn == inputParam.pHALParam->AELock)
                {
                    m_outputParam.pProcessStatsOutput->AEState = ControlAEStateLocked;
                }
                else
                {
                    m_outputParam.pProcessStatsOutput->AEState = ControlAEStateConverged;
                }
            }
        }

        // Update all of AEC Trace Events required
        if (CamxResultSuccess == result)
        {
            UpdateTraceEvents(m_outputParam.pProcessStatsOutput);
        }
    }

    result = PublishMetadata(m_outputParam.pProcessStatsOutput, &m_HALParam);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::VendorTagListAllocation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::VendorTagListAllocation()
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    CamxResult              result              = CamxResultSuccess;
    StatsVendorTagInfoList* pVendorTagInfoList  = NULL;

    AECCommandInputParam  inputParam    = { 0 };
    AECCommandOutputParam outputParam   = { 0 };

    outputParam.pVendorTagInfoList = &m_vendorTagInfoOutputputList;
    inputParam.pAlgorithmInput = &m_algoInput;

    result = m_pAECEngine->HandleCommand(AECCommand::GetPubVendorTagFromAlgo, &inputParam, &outputParam);

    if (CamxResultSuccess != result)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Algorithm library does not support Vendor Tag");
        result = CamxResultSuccess;
    }
    else
    {
        pVendorTagInfoList = static_cast<StatsVendorTagInfoList*>(outputParam.pVendorTagInfoList);
        // allocate memory to hold each vendor tag's data
        result = AllocateMemoryVendorTag(pVendorTagInfoList);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::AddOEMDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::AddOEMDependencies(
    const StatsProcessRequestData*  pStatsProcessRequestDataInfo,
    StatsDependency*                pStatsDependency)
{
    CamxResult              result              = CamxResultSuccess;
    StatsVendorTagInfoList* pVendorTagInfoList  = NULL;

    AECCommandInputParam  inputParam    = { 0 };
    AECCommandOutputParam outputParam   = { 0 };

    outputParam.pVendorTagInfoList = &m_vendorTagInputList;
    inputParam.pAlgorithmInput = &m_algoInput;

    result = m_pAECEngine->HandleCommand(AECCommand::GetVendorTagFromAlgo, &inputParam, &outputParam);

    if (CamxResultSuccess != result)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Algorithm library does not support Vendor Tag");
        result = CamxResultSuccess;
    }
    else
    {
        pVendorTagInfoList = static_cast<StatsVendorTagInfoList*>(outputParam.pVendorTagInfoList);
        if (NULL != pVendorTagInfoList)
        {
            UINT32  propertyCount = Utils::MinUINT32(pVendorTagInfoList->vendorTagCount,
                                                     (MaxStatsProperties - pStatsDependency->propertyCount));

            // We can have only MaxStatsProperties number of properties that Stats node can be dependent on
            // Adding vendor tag dependencies
            for (UINT32 i = 0; i < propertyCount; i++)
            {
                pStatsDependency->properties[pStatsDependency->propertyCount + i].property =
                    pVendorTagInfoList->vendorTag[i].vendorTagId;

                CAMX_LOG_VERBOSE(CamxLogGroupAEC,
                    "Added Vendor Tag(%u) to the dependent list. requestID(%llu)",
                    pVendorTagInfoList->vendorTag[i].vendorTagId,
                    pStatsProcessRequestDataInfo->requestId);
            }

            pStatsDependency->propertyCount += propertyCount;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetDependencies(
    const StatsProcessRequestData*  pStatsProcessRequestDataInfo,
    StatsDependency*                pStatsDependency)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    CamxResult result = CamxResultSuccess;

    // Ensure ordering
    if (FirstValidRequestId < pStatsProcessRequestDataInfo->requestId)
    {
        pStatsDependency->properties[pStatsDependency->propertyCount++] =
        { PropertyIDAECStatsControl, 1,  StatsDependencyGroup::GROUP_NORMAL };
    }

    if (TRUE == pStatsProcessRequestDataInfo->skipProcessing)
    {
        // We need to publish previous frame data to property pool when skipProcessing flag is set.
        // Add previous frame(offset = 1) AEC Frame/Stats Control properties as dependency.
        pStatsDependency->properties[pStatsDependency->propertyCount++] =
        { PropertyIDAECFrameControl, 1,  StatsDependencyGroup::GROUP_NORMAL };
        // stats control already inserted for ordering above
    }
    else
    {
        result = AddOEMDependencies(pStatsProcessRequestDataInfo, pStatsDependency);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::~CAECStatsProcessor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAECStatsProcessor::~CAECStatsProcessor()
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    if (NULL != m_pAECEngine)
    {
        FILE* pWarmStartAEC = NULL;
        CHAR  pFilePath[FILENAME_MAX];

        OsUtils::SNPrintF(pFilePath,
                          FILENAME_MAX,
                          "%s/aecWarmStartCamera_%d.txt", ConfigFileDirectory,
                          m_cameraId);

        pWarmStartAEC = OsUtils::FOpen(pFilePath, "w");
        if (NULL != pWarmStartAEC)
        {
            // Write current sensitivities
            OsUtils::FPrintF(pWarmStartAEC, "%f %f %f\n",
                m_engineStatsOutput.algorithmOutput.engineFrameControl.exposureData[AECAlgoExposureShort].sensitivity,
                m_engineStatsOutput.algorithmOutput.engineFrameControl.exposureData[AECAlgoExposureLong].sensitivity,
                m_engineStatsOutput.algorithmOutput.engineFrameControl.exposureData[AECAlgoExposureSafe].sensitivity);

            OsUtils::FClose(pWarmStartAEC);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupStats, "Error! Failed to write sensitivity data to file");
        }

        m_pAECEngine->Destroy();
        m_pAECEngine = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetVendorTags
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetVendorTags(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    AECEngineAlgorithmInput*       pInput)
{
    CamxResult      result = CamxResultSuccess;
    UINT64          requestId     = pStatsProcessRequestDataInfo->requestId;

    CAMX_ASSERT(NULL != pInput);

    Utils::Memset(&pInput->vendorTagInputList, 0, sizeof(pInput->vendorTagInputList));

    for (UINT32 i = 0; i < m_vendorTagInputList.vendorTagCount; i++)
    {
        UINT   pVendorTag[1]       = { m_vendorTagInputList.vendorTag[i].vendorTagId };
        UINT64 pVendorTagOffset[1] = { 0 };

        m_pNode->GetDataList(pVendorTag, &pInput->vendorTagInputList.vendorTag[i].pData, pVendorTagOffset, 1);

        pInput->vendorTagInputList.vendorTag[i].vendorTagId = pVendorTag[0];
        pInput->vendorTagInputList.vendorTag[i].dataSize =
            static_cast<UINT32>(HAL3MetadataUtil::GetMaxSizeByTag(pVendorTag[0]));

        // Currently DRQ handles property/metadata dependency only on current request.
        pInput->vendorTagInputList.vendorTag[i].appliedDelay = 0;

        if ((pInput->vendorTagInputList.vendorTag[i].pData != NULL) && (pInput->vendorTagInputList.vendorTag[i].dataSize > 0))
        {
            pInput->vendorTagInputList.vendorTagCount++;
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC,
                          "Failed to retrieve input vendor tag data. Id: %u pData:%p dataSize:%u reqID: %llu",
                           pVendorTag[0],
                           pInput->vendorTagInputList.vendorTag[i].pData,
                           pInput->vendorTagInputList.vendorTag[i].dataSize,
                           requestId);

            result = CamxResultEFailed;
            break;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetChiStatsSession
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetChiStatsSession(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    AECEngineAlgorithmInput*       pInput)
{
    pInput->statsSession.SetStatsProcessorRequestData(pStatsProcessRequestDataInfo);

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetAlgoInput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetAlgoInput(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    AECEngineAlgorithmInput*       pInput,
    AECEngineHALParam*             pHALParam)
{
    CAMX_UNREFERENCED_PARAM(pHALParam);

    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    CAMX_ASSERT(NULL != pInput);
    CAMX_ASSERT(NULL != pStatsProcessRequestDataInfo);

    CamxResult result = CamxResultSuccess;

    static const UINT StatsData[] =
    {
        PropertyIDISPHDRBEConfig,
        PropertyIDParsedHDRBEStatsOutput,
        PropertyIDParsedHDRBHistStatsOutput,
        PropertyIDParsedBHistStatsOutput
    };
    static const UINT StatsLength                  = CAMX_ARRAY_SIZE(StatsData);
    VOID*             pData[StatsLength]           = { 0 };
    UINT64            statsDataOffset[StatsLength] = { m_pNode->GetStaticSettings()->maxPipelineDelay, 0, 0, 0 };

    m_pNode->GetDataList(StatsData, pData, statsDataOffset, StatsLength);

    // Populate the frame number for core to use
    pInput->requestId = pStatsProcessRequestDataInfo->requestId;

    // Check for presence of BE stats
    if (NULL != pData[1])
    {
        if (NULL != pData[0])
        {
            // Save the BE stats inside core's input structure
            SetAlgoBayerGridValue(reinterpret_cast<PropertyISPHDRBEStats*>(pData[0]),
                                  reinterpret_cast<ParsedHDRBEStatsOutput*>(*static_cast<VOID**>(pData[1])),
                                  &pInput->bayerGrid);
        }
        else
        {
            CAMX_LOG_WARN(CamxLogGroupAEC,
                          "PropertyID %08x (ISP HDR-BE Config) has not been published! ",
                           PropertyIDISPHDRBEConfig);
        }
    }
    else
    {
        CAMX_LOG_WARN(CamxLogGroupAEC,
                        "PropertyID %08x (AEC HDR-BE Stats) has not been published! ",
                        PropertyIDParsedHDRBEStatsOutput);
        /// @todo  (CAMX-523): determine if this should be considered a failure or not; for now consider success
    }

    // Dual BHIST support
    if (NULL != pData[2])
    {
        SetAlgoBayerHDRBHistValue(reinterpret_cast<ParsedHDRBHistStatsOutput*>(*static_cast<VOID**>(pData[2])),
                                  &pInput->bayerHist);
    }
    else
    {
        CAMX_LOG_WARN(CamxLogGroupAEC,
                     "PropertyID %d (ISP HDR-BHist Stats) has not been published! ",
                     PropertyIDParsedHDRBHistStatsOutput);
    }
    if (NULL != pData[3])
    {
        SetAlgoBayerHistValue(reinterpret_cast<ParsedBHistStatsOutput*>(*static_cast<VOID**>(pData[3])),
            &pInput->hdrBHist);
    }
    else
    {
        CAMX_LOG_WARN(CamxLogGroupAEC,
                      "PropertyID %d (ISP BHist Stats) has not been published! ",
                      PropertyIDParsedBHistStatsOutput);
    }

    if (CamxResultSuccess == result)
    {
        result = GetVendorTags(pStatsProcessRequestDataInfo, pInput);
    }

    if (CamxResultSuccess == result)
    {
        result = GetChiStatsSession(pStatsProcessRequestDataInfo, pInput);
    }

    if (CamxResultSuccess == result)
    {
        SetDebugDataPointer(pStatsProcessRequestDataInfo, pInput);
    }

    if (CamxResultSuccess == result)
    {
        result = SetCameraInformation(pStatsProcessRequestDataInfo, pInput);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetDebugDataPointer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::SetDebugDataPointer(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    AECEngineAlgorithmInput*       pInput)
{
    DebugData*  pDebugData  = NULL;
    UINT64      requestId   = pStatsProcessRequestDataInfo->requestId;

    // Only use debug data on the master camera
    if ((StatsAlgoRoleDefault   == pStatsProcessRequestDataInfo->cameraInfo.algoRole)   ||
        (StatsAlgoRoleMaster    == pStatsProcessRequestDataInfo->cameraInfo.algoRole))
    {
        (void)StatsUtil::GetDebugDataBuffer(m_pDebugDataPool, requestId, PropertyIDDebugDataAEC, &pDebugData);
    }

    if (NULL != pDebugData)
    {
        pInput->debugData.dataSize  = static_cast<UINT32>(pDebugData->size);
        pInput->debugData.pData     = pDebugData->pData;
    }
    else
    {
        pInput->debugData.dataSize  = 0;
        pInput->debugData.pData     = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetCameraInformation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetCameraInformation(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    AECEngineAlgorithmInput*       pInput)
{
    CAMX_ASSERT(pStatsProcessRequestDataInfo != NULL );
    CAMX_ASSERT(pInput != NULL );

    pInput->cameraInfo.algoRole   = pStatsProcessRequestDataInfo->cameraInfo.algoRole;
    pInput->cameraInfo.cameraId   = pStatsProcessRequestDataInfo->cameraInfo.cameraId;
    pInput->cameraInfo.cameraType = pStatsProcessRequestDataInfo->cameraInfo.cameraType;

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetAlgoChromatix
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetAlgoChromatix(
    ChiTuningModeParameter* pInputTuningModeData)
{
    CamxResult              result      = CamxResultSuccess;
    AECCommandInputParam    inputParam  = { 0 };
    AECCommandOutputParam   outputParam = { 0 };

    CAMX_ASSERT(m_tuningData.pTuningSetManager != NULL );

    if (NULL != pInputTuningModeData)
    {
        m_tuningData.pTuningModeSelectors   = reinterpret_cast<TuningMode*>(&pInputTuningModeData->TuningMode[0]);
        m_tuningData.numSelectors           = pInputTuningModeData->noOfSelectionParameter;
        inputParam.pTuningData              = &m_tuningData;
        result = m_pAECEngine->HandleCommand(AECCommand::SetChromatix, &inputParam, &outputParam);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetAlgoSetParams
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetAlgoSetParams(
    AECEngineHALParam* pHALParam)
{
    CamxResult              result         = CamxResultSuccess;
    AECCommandInputParam    inputParam     = { 0 };
    AECCommandOutputParam   outputParam    = { 0 };
    StatsRectangle          cropWindow     = { 0 };
    AECEngineNodesUpdate    AECNodesUpdate = { 0 };
    AFOutput                AFOutput       = {};
    AWBOutputInternal       AWBoutput      = {};
    AWBFrameInfo            AWBInfo        = {};

    if (CamxResultSuccess == result)
    {
        inputParam.pHALParam = pHALParam;
        result = m_pAECEngine->HandleCommand(AECCommand::SetPerFrameControlParam, &inputParam, &outputParam);
    }

    // Set updates from other Stats Nodes: AF, AWB, AFD, ASD
    AFOutput.status.status            = AFStatusInitialized;
    AWBoutput.flashEstimationStatus   = AWBFlashEstimationState::AWBEstimationInactive;
    AECNodesUpdate.pAFOutput          = &AFOutput;
    AECNodesUpdate.pAWBOutputInternal = &AWBoutput;
    AECNodesUpdate.pAWBFrameInfo      = &AWBInfo;
    result = ReadStatsNodesUpdates(&AECNodesUpdate);
    if (CamxResultSuccess == result)
    {
        inputParam.pNodesUpdate = &AECNodesUpdate;
        result = m_pAECEngine->HandleCommand(AECCommand::SetNodesUpdate, &inputParam, &outputParam);
    }
    // Set the stats window, which could change with zoom
    if (CamxResultSuccess == result)
    {
        GetCropWindow(&cropWindow);
        inputParam.pCropWindow = &cropWindow;

        result = m_pAECEngine->HandleCommand(AECCommand::ProcessCropWindow, &inputParam, &outputParam);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CAECStatsProcessor::PublishVendorTagMetadata()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishVendorTagMetadata(
    StatsVendorTagList*             pVendorTagOutput)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pVendorTagOutput)
    {
        for (UINT32 i = 0; i < pVendorTagOutput->vendorTagCount; i++)
        {
            if (0 == pVendorTagOutput->vendorTag[i].sizeOfWrittenData)
            {
                continue;
            }

            static const UINT   WriteProps[]                             = {pVendorTagOutput->vendorTag[i].vendorTagId};
            const VOID*         pOutputData[CAMX_ARRAY_SIZE(WriteProps)] = { pVendorTagOutput->vendorTag[i].pData };
            UINT                pDataCount[CAMX_ARRAY_SIZE(WriteProps)]  = { 1 };

            result = m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));

            CAMX_LOG_VERBOSE(CamxLogGroupAEC,
                            "Published VendorTag(%u) size(%d) pData(%p). Result = %d",
                             pVendorTagOutput->vendorTag[i].vendorTagId,
                             pVendorTagOutput->vendorTag[i].dataSize,
                             pVendorTagOutput->vendorTag[i].pData,
                             result);

            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupAEC, "Failed to publish vendor tag %u. Result = %d", WriteProps[0], result);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::AllocateMemoryVendorTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::AllocateMemoryVendorTag(
    StatsVendorTagInfoList* pVendorTagInfoList)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
    CAMX_ASSERT_MESSAGE(NULL != pVendorTagInfoList, "pVendorTagInfoList NULL pointer");

    CamxResult              result = CamxResultSuccess;

    Utils::Memset(&m_algoVendorTagOutputList, 0, sizeof(m_algoVendorTagOutputList));
    for (UINT32 j = 0; j < pVendorTagInfoList->vendorTagCount; j++)
    {
        SIZE_T size = HAL3MetadataUtil::GetMaxSizeByTag(pVendorTagInfoList->vendorTag[j].vendorTagId);
        if (0 != size)
        {
            m_algoVendorTagOutputList.vendorTag[j].vendorTagId  = pVendorTagInfoList->vendorTag[j].vendorTagId;
            m_algoVendorTagOutputList.vendorTag[j].appliedDelay = pVendorTagInfoList->vendorTag[j].appliedDelay;
            m_algoVendorTagOutputList.vendorTag[j].pData        = CAMX_CALLOC(size);

            if (NULL == m_algoVendorTagOutputList.vendorTag[j].pData)
            {
                CAMX_LOG_ERROR(CamxLogGroupAEC,
                               "Allocating memory for vendor tagID[%d]: %d failed.",
                                j,
                                pVendorTagInfoList->vendorTag[j].vendorTagId);
                result = CamxResultENoMemory;
            }
            else
            {
                m_algoVendorTagOutputList.vendorTag[j].dataSize = static_cast<UINT32>(size);
                m_algoVendorTagOutputList.vendorTagCount++;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishMetadata(
    AECEngineProcessStatsOutput*   pOutput,
    AECEngineHALParam*             pHALParam)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
    CamxResult result = PublishPropertyPoolFrameControl(pOutput);

    if (CamxResultSuccess == result)
    {
        result = PublishPropertyPoolFrameInfo(pOutput, pHALParam);
    }

    if (CamxResultSuccess == result)
    {
        result = PublishExternalCameraMetadata(pOutput);
    }

    if (CamxResultSuccess == result)
    {
        result = PublishPropertyPoolInternal(pOutput);
    }

    if (CamxResultSuccess == result)
    {
        result = PublishPropertyDebugData();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::OverwriteAECOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::OverwriteAECOutput(
    AECEngineProcessStatsOutput* pOutput
    ) const
{
    CAMX_ASSERT(NULL != pOutput);

    AECAlgoFrameInfo*     pAlgoFrameInfo     = NULL;
    AECAlgoFrameControl*  pAlgoFrameControl  = NULL;
    AECAlgoAPEXData*      pAPEXData          = NULL;
    UINT32*               pLEDCurrents       = NULL;
    AECAlgoExposureData*  pExposureData      = NULL;
    const StaticSettings* pStaticSettings    = HwEnvironment::GetInstance()->GetStaticSettings();

    pAlgoFrameInfo      = &pOutput->algorithmOutput.frameInfo;
    pAlgoFrameControl   = &pOutput->algorithmOutput.engineFrameControl.frameControl;
    pAPEXData           = &pOutput->algorithmOutput.engineFrameControl.apexData;
    pLEDCurrents        = &pOutput->algorithmOutput.engineFrameControl.LEDCurrents[0];
    pExposureData       = &pOutput->algorithmOutput.engineFrameControl.exposureData[0];

    // Populate the defaults for Frame Info
    pAlgoFrameInfo->brightnessSettled       = TRUE;
    pAlgoFrameInfo->aecSettled              = TRUE;
    pAlgoFrameInfo->asdExtremeBlueRatio     = 1.0f;
    pAlgoFrameInfo->asdExtremeGreenRatio    = 1.0f;
    pAlgoFrameInfo->snapshotIndicator       = AECAlgoSnapshotNormal;
    pAlgoFrameInfo->touchEVIndicator        = AECAlgoTouchEVInactive;
    pAlgoFrameInfo->LEDAFRequired           = FALSE;
    CamX::Utils::Memset(&pAlgoFrameInfo->legacyYStats[0], 0, sizeof(pAlgoFrameInfo->legacyYStats));

    // Populate the defaults for Frame Control
    pAlgoFrameControl->luxIndex             = pStaticSettings->luxIndex;
    pAlgoFrameControl->flashState           = AECAlgoFlashStateOff;
    pAlgoFrameControl->ISOValue             = 100;
    pAlgoFrameControl->LEDBGRatio           = 1.0f;
    pAlgoFrameControl->LEDRGRatio           = 1.0f;
    pAlgoFrameControl->LEDInfluenceRatio    = 1.0f;
    pAlgoFrameControl->LEDFirstEntryRatio   = 1.0f;
    pAlgoFrameControl->LEDLastEntryRatio    = 0.0f;

    // Populate the defaults for APEX Data
    pAPEXData->brightnessValue = 0.0f;
    pAPEXData->apertureValue   = 0.0f;
    pAPEXData->speedValue      = 0.0f;
    pAPEXData->timeValue       = 0.0f;
    pAPEXData->exposureValue   = 0.0f;

    // Populate the defaults for LED Currents
    pLEDCurrents[LEDSetting1]  = 0;
    pLEDCurrents[LEDSetting2]  = 0;

    // Populate the defaults for Exposure Data
    for (UINT32 i = 0; i < ExposureIndexCount; i++)
    {
        pExposureData[i].linearGain   = pStaticSettings->exposureGain;
        pExposureData[i].exposureTime = pStaticSettings->exposureTime;
        pExposureData[i].sensitivity  = pStaticSettings->exposureTime * pStaticSettings->exposureGain;
    }

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishPropertyPoolFrameControl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishPropertyPoolFrameControl(
    AECEngineProcessStatsOutput*   pEngineOutput)
{
    AECFrameControl   frameControl                             = {{{0}}};
    AECStatsControl   statsControl                             = {{{0}}};
    VOID*             pPeerInfo                                = NULL;
    static const UINT WriteProps[]                             =
    {
        PropertyIDAECFrameControl,
        PropertyIDAECStatsControl,
        PropertyIDAECPeerInfo,
    };

    const VOID* pOutputData[CAMX_ARRAY_SIZE(WriteProps)] =
    {
        &frameControl,
        &statsControl,
        &pPeerInfo,
    };

    UINT pDataCount[CAMX_ARRAY_SIZE(WriteProps)]  =
    {
        sizeof(frameControl),
        sizeof(statsControl),
        sizeof(VOID*)
    };

    // Populate AEC update for normal/preview frame control
    PopulatePropPoolFrameControl(pEngineOutput, &frameControl);
    // Populate AEC update for stats control
    PopulatePropPoolStatsControl(pEngineOutput, &statsControl);
    PopulatePropPoolPeerControl(&m_engineStatsOutput, &pPeerInfo);

    CAMX_LOG_VERBOSE(CamxLogGroupAEC,
                     "AEC: Publish FrameControl for ReqId=%llu camID:%d Gain=%f ExpTime=%llu LuxIndex=%f"
                     " LEDInfluenceRatio=%f LEDFirstEntryRatio=%f LEDLastEntryRatio=%f "
                     " FlashType=%d PreFlashState=%d LEDCurrents=%d:%d HxV = %dx%di pInfo:%p",
                     m_currProcessingRequestId,
                     m_cameraId,
                     frameControl.exposureInfo[0].linearGain,
                     frameControl.exposureInfo[0].exposureTime,
                     frameControl.luxIndex,
                     frameControl.LEDInfluenceRatio,
                     frameControl.LEDFirstEntryRatio,
                     frameControl.LEDLastEntryRatio,
                     frameControl.flashInfo,
                     frameControl.preFlashState,
                     frameControl.LEDCurrents[0],
                     frameControl.LEDCurrents[1],
                     statsControl.statsConfig.BEConfig.horizontalNum,
                     statsControl.statsConfig.BEConfig.verticalNum,
                     pPeerInfo);

    return m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishPropertyPoolAdditionalFrameControl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishPropertyPoolAdditionalFrameControl(
    AECEngineProcessStatsOutput*   pOutput)
{
    UINT32 metaTag = 0;

    CamxResult result = VendorTagManager::QueryVendorTagLocation("org.quic.camera2.sensor_register_control",
                                                                 "sensor_register_control",
                                                                 &metaTag);

    if (CamxResultSuccess == result)
    {
        static const UINT WriteProps[]                             = {metaTag};

        const VOID* pOutputData[CAMX_ARRAY_SIZE(WriteProps)] =
        {
            &pOutput->algorithmOutput.engineAdditionalControl.sensorControl
        };

        UINT pDataCount[CAMX_ARRAY_SIZE(WriteProps)] =
        {
            sizeof(SensorRegisterControl)/sizeof(INT32)
        };

        // pOutput->algorithmOutput.engineAdditionalControl.sensorControl = { {10} };

        result = m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));
    }

    return result;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishPropertyPoolFrameInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishPropertyPoolFrameInfo(
    AECEngineProcessStatsOutput*   pOutput,
    AECEngineHALParam*             pHALParam)
{
    CAMX_ASSERT(NULL != pOutput);

    CamxResult          result        = CamxResultSuccess;
    UINT                metaTagFlash  = 0;
    UINT                metaTagLLS    = 0;
    AECFrameInformation frameInfo     = {};
    UINT                metaLuxTag = 0;

    PopulatePropPoolFrameInfo(pOutput, &frameInfo, pHALParam);

    CAMX_LOG_VERBOSE(CamxLogGroupAEC,
                     "AEC: Publish FrameInfo for ReqId=%llu Settled=%d Lux=%f Preflash=%d SnapshotInd=%d BrightnessValue=%f "
                     "FL:%f ",
                     m_currProcessingRequestId,
                     frameInfo.AECSettled,
                     frameInfo.luxIndex,
                     frameInfo.AECPreFlashState,
                     frameInfo.snapshotIndicator,
                     frameInfo.brightnessValue,
                     frameInfo.frameLuma);

    result = VendorTagManager::QueryVendorTagLocation("com.qti.stats_control", "is_flash_snapshot", &metaTagFlash);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: is_flash_snapshot");

    result = VendorTagManager::QueryVendorTagLocation("com.qti.stats_control", "is_lls_needed", &metaTagLLS);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: is_lls_needed");

    result = VendorTagManager::QueryVendorTagLocation("com.qti.chi.statsaec", "AecLux", &metaLuxTag);
    CAMX_ASSERT_MESSAGE(CamxResultSuccess == result, "Failed to get vendor tag: AecLux");

    INT32              isFlashRequired       = (SnapshotTypeFlash == frameInfo.snapshotIndicator) ? 1 : 0;
    INT32              isLLSNedded           = (SnapshotTypeLLS   == frameInfo.snapshotIndicator) ? 1 : 0;
    static const UINT  WriteProps[]          = { PropertyIDAECFrameInfo, metaTagFlash, metaTagLLS, metaLuxTag };
    const UINT         propSize              = CAMX_ARRAY_SIZE(WriteProps);
    const VOID*        pOutputData[propSize] = { &frameInfo, &isFlashRequired, &isLLSNedded, &frameInfo.luxIndex };
    UINT               pDataCount[propSize]  = { sizeof(frameInfo), 1, 1, 1 };

    return m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishExternalCameraMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishExternalCameraMetadata(
    AECEngineProcessStatsOutput*    pOutput)
{
    CamxResult            result          = CamxResultSuccess;
    const StaticSettings* pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();
    UINT8                 mode            = static_cast<UINT8>(pOutput->AEMode);
    BOOL                  AELockFlag      = (pOutput->AEState == ControlAEStateLocked) ? TRUE : FALSE;
    UINT8                 state           = static_cast<UINT8>(pOutput->AEState);
    // For AE state, the app monitors the transition of the AE state from converged to
    // locked state so hardcode to lock will not work.However, we can make ae state to
    // NULL which will cause APP to ignore the AE state.So, please, put AE state to NULL
    // until ae algo is available.
    VOID*                 pState       = (TRUE == pStaticSettings->force3ALockedResult) ? NULL : &state;
    UINT32                activeWidth  = m_hardwareInfo.sensorInfo.sensorActiveResWidth;
    UINT32                activeHeight = m_hardwareInfo.sensorInfo.sensorActiveResHeight;
    UINT32                resWidth     = m_hardwareInfo.sensorInfo.sensorResWidth;
    UINT32                resHeight    = m_hardwareInfo.sensorInfo.sensorResHeight;
    FLOAT                 widthRatio   = (static_cast<FLOAT>(activeWidth) / resWidth);
    FLOAT                 heightRatio  = (static_cast<FLOAT>(activeHeight) / resHeight);
    StatsRectangle        statsROI     = { 0 };
    UINT8                 triggerValue;
    Rational              exposureCompensationStep;
    UINT8                 flickerMode;
    UINT8                 aeAntiBandingMode;

    // Set Pre-capture Trigger
    ControlAEPrecaptureTriggerValues precaptureTrigger;
    switch (pOutput->AEPrecapTrigger)
    {
        case PrecapTriggerStart:
            precaptureTrigger = ControlAEPrecaptureTriggerStart;
            break;

        case PrecapTriggerCancel:
            precaptureTrigger = ControlAEPrecaptureTriggerCancel;
            break;

        case PrecapTriggerIdle:
        default:
            precaptureTrigger = ControlAEPrecaptureTriggerIdle;
            break;
    }

    triggerValue                         = static_cast<UINT8>(precaptureTrigger);
    exposureCompensationStep.numerator   = CompensationStepNumerator;
    exposureCompensationStep.denominator = CompensationStepDenominator;

    // Get crop window in CAMIF size
    GetCropWindow(&statsROI);
    // Set AE Regions based on crop window
    pOutput->AERegion.xMin   = Utils::RoundFLOAT((m_pAECEngine->m_HALParam.touchROI.x *
        statsROI.width * widthRatio) + (statsROI.left * widthRatio));
    pOutput->AERegion.yMin   = Utils::RoundFLOAT((m_pAECEngine->m_HALParam.touchROI.y *
        statsROI.height * heightRatio) + (statsROI.top * heightRatio));
    pOutput->AERegion.xMax   = pOutput->AERegion.xMin +
        Utils::RoundFLOAT(m_pAECEngine->m_HALParam.touchROI.dx * statsROI.width * widthRatio);
    pOutput->AERegion.yMax   = pOutput->AERegion.yMin +
        Utils::RoundFLOAT(m_pAECEngine->m_HALParam.touchROI.dy * statsROI.height * heightRatio);
    pOutput->AERegion.weight = static_cast<INT32>(m_pAECEngine->m_HALParam.touchROI.weight);

    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Publish CropWindow xMin, yMin, xMax, yMax = %d, %d, %d, %d",
        pOutput->AERegion.xMin,
        pOutput->AERegion.yMin,
        (pOutput->AERegion.xMax - pOutput->AERegion.xMin),
        (pOutput->AERegion.yMax - pOutput->AERegion.yMin));

    // Set Antibanding mode
    // AEC calculated exposure time should be always align to published scene flicker
    // Due to this AEC needs to publish scene flicker mode.
    switch (m_pAECEngine->m_HALParam.AEAntibandingModeValue)
    {
        case StatisticsAntibandingOff:
            flickerMode = StatisticsSceneFlickerNone;
            break;
        case StatisticsAntibanding50Hz:
            flickerMode = StatisticsSceneFlicker50Hz;
            break;
        case StatisticsAntibanding60Hz:
            flickerMode = StatisticsSceneFlicker60Hz;
            break;
        default:
            flickerMode = StatisticsSceneFlickerNone;
            break;
    }
    aeAntiBandingMode = static_cast<UINT8>(m_pAECEngine->m_HALParam.AEAntibandingModeValue);

    static const UINT WriteProps[] =
    {
        ControlAEExposureCompensation,
        ControlAELock,
        ControlAEMode,
        ControlAERegions,
        ControlAEState,
        ControlAEPrecaptureTrigger,
        ControlMode,
        ControlAECompensationStep,
        StatisticsSceneFlicker,
        ControlAEAntibandingMode,
        ControlAETargetFpsRange
    };

    const VOID* pOutputData[CAMX_ARRAY_SIZE(WriteProps)] =
    {
        &pOutput->AECompensation,
        &AELockFlag,
        &mode,
        &pOutput->AERegion,
        pState,
        &triggerValue,
        &pOutput->controlMode,
        &exposureCompensationStep,
        &flickerMode,
        &aeAntiBandingMode,
        &pOutput->currentFPSRange
    };
    UINT pDataCount[CAMX_ARRAY_SIZE(WriteProps)]  =
    {
        sizeof(pOutput->AECompensation) / sizeof(INT32),
        sizeof(AELockFlag) / sizeof(BOOL),
        sizeof(ControlAEModeValues) / sizeof(INT32),
        AERegionSize,
        sizeof(ControlAEStateValues)/sizeof(INT32),
        sizeof(ControlAEPrecaptureTriggerValues)/sizeof(INT32),
        sizeof(ControlModeValues)/sizeof(INT32),
        1,
        sizeof(flickerMode)/ sizeof(UINT8),
        sizeof(aeAntiBandingMode)/ sizeof(UINT8),
        sizeof(pOutput->currentFPSRange) / sizeof(INT32)
    };
    result = m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));

    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Published AEC Meta:camId:%d ReqId=%llu AECompensation:%d AELock:%d AEMode:%d,"
        "AE State:%d AE Precapture trigger:%d, ExposureCompStep:%d flickerMode:%d, "
        "FPS Range(min:%d max:%d)",
        m_cameraId,
        m_currProcessingRequestId, pOutput->AECompensation, AELockFlag, mode, state, triggerValue,
        exposureCompensationStep.denominator, flickerMode, pOutput->currentFPSRange.min,
        pOutput->currentFPSRange.max);

    if (CamxResultSuccess == result)
    {
        // Publishing Additional frame control output from the algorithm to the future metadata slot
        result = PublishPropertyPoolAdditionalFrameControl(pOutput);
    }

    if (CamxResultSuccess == result)
    {
        // Need to fix this
        result = PublishVendorTagMetadata(&pOutput->vendorTagList);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishPropertyPoolInternal
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishPropertyPoolInternal(
    AECEngineProcessStatsOutput*    pOutput)
{
    CAMX_ASSERT(NULL != pOutput);

    AECOutputInternal   output                                   = {{0}};
    static const UINT   WriteProps[]                             = {PropertyIDAECInternal};
    const VOID*         pOutputData[CAMX_ARRAY_SIZE(WriteProps)] = { &output };
    UINT                pDataCount[CAMX_ARRAY_SIZE(WriteProps)]  = { sizeof(output) };

    PopulatePropPoolInternal(pOutput, &output);
    CAMX_LOG_VERBOSE(CamxLogGroupAEC,
                     "AEC: Publish Internal MetaData for ReqId=%llu FlashInfo=%d PreFlashState=%d "
                     "LEDInfluenceRatio=%f RG:BG Ratio=%f:%f",
                     m_currProcessingRequestId,
                     output.flashInfo,
                     pOutput->preFlashState,
                     output.LEDInfluenceRatio,
                     output.LEDRGRatio,
                     output.LEDBGRatio);

    return m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishPropertyDebugData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishPropertyDebugData()
{
    CamxResult          result                      = CamxResultSuccess;
    UINT32              metaTag                     = 0;
    static const UINT   PropertiesDebugData[]       = { PropertyIDDebugDataAll };
    VOID*               pSrcData[1]                 = { 0 };
    UINT                length                      = CAMX_ARRAY_SIZE(PropertiesDebugData);
    UINT64              propertyDebugDataOffset[1]  = { 0 };

    // Make a copy in main metadata pool
    m_pNode->GetDataList(PropertiesDebugData, pSrcData, propertyDebugDataOffset, length);

    // Current framework implementation support only posting DebugDataAll
    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera.debugdata", "DebugDataAll", &metaTag);
    if (CamxResultSuccess == result)
    {
        const UINT  DebugDataVendorTag[]    = { metaTag };
        const VOID* pDstData[1]             = { pSrcData[0] };
        UINT        pDataCount[1]           = { sizeof(DebugData) };
        result = m_pNode->WriteDataList(DebugDataVendorTag, pDstData, pDataCount, CAMX_ARRAY_SIZE(DebugDataVendorTag));
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishCrossProperty
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishCrossProperty(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo)
{
    CamxResult result = CamxResultSuccess;

    // Publish AEC cross pipeline property for dual camera usecase.
    UINT64              requestId                                   = pStatsProcessRequestDataInfo->requestId;
    static const UINT   pWriteProps[]                               = { PropertyIDCrossAECStats };
    const VOID*         pOutputData[CAMX_ARRAY_SIZE(pWriteProps)]   = { &requestId };
    UINT                pDataCount[CAMX_ARRAY_SIZE(pWriteProps)]    = { sizeof(requestId) };

    result = m_pNode->WriteDataList(pWriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(pWriteProps));
    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Published PropertyIDCrossAECStats:%X for Req:%llu result:%d",
                     PropertyIDCrossAECStats, requestId, result);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PublishPreRequestOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PublishPreRequestOutput(
    MetadataPool* pUsecasePool)
{
    CamxResult                  result                = CamxResultSuccess;
    AECCommandInputParam        inputParam            = {0};
    AECCommandOutputParam       outputParam           = {0};
    AECEngineFrameControl       engineFrameControl    = {};
    StatsStreamInitConfig       statsStreamInitConfig = {};
    AECFrameControl             usecaseFrameControl   = {{{0}}};
    AECStatsControl             usecaseStatsControl   = {{{0}}};
    AECStatsControl             statsControl          = {{{0}}};
    AECFrameControl             frameControl          = {{{0}}};

    // Query the algorithm for the start streaming parameter
    // Read from use case pool to check if we have results from Fast AEC
    statsStreamInitConfig.operationMode = StatsOperationModeNormal;
    result = StatsUtil::GetStatsStreamInitConfig(pUsecasePool, &statsStreamInitConfig);

    inputParam.pInitStreamConfig             = &statsStreamInitConfig;
    outputParam.pFrameControl                = &engineFrameControl;

    result = m_pAECEngine->HandleCommand(AECCommand::StartStreaming, &inputParam, &outputParam);

    if (CamxResultSuccess != result)
    {
        CAMX_LOG_ERROR(CamxLogGroupAEC, "Failed to query start exposure parameters. result: %s", CamxResultStrings[result]);
        return result;
    }

    // Populate PropertyIDUsecaseAECFrameControl from the engine command output
    usecaseFrameControl.luxIndex = engineFrameControl.frameControl.luxIndex;
    CAMX_STATIC_ASSERT(AECAlgoExposureCount == ExposureIndexCount);
    for (UINT32 i = 0; i < ExposureIndexCount; i++)
    {
        usecaseFrameControl.exposureInfo[i].deltaEVFromTarget = engineFrameControl.exposureData[i].deltaEVFromTarget;
        usecaseFrameControl.exposureInfo[i].exposureTime      = engineFrameControl.exposureData[i].exposureTime;
        usecaseFrameControl.exposureInfo[i].linearGain        = engineFrameControl.exposureData[i].linearGain;
        usecaseFrameControl.exposureInfo[i].sensitivity       = engineFrameControl.exposureData[i].sensitivity;
    }

    // Populate PropertyIDUsecaseAECStatsControl from the engine command output
    SetStatsConfigFromAlgoConfig(&(engineFrameControl.statsConfig),
                                 &(engineFrameControl.statsBHISTConfig),
                                 &(usecaseStatsControl.statsConfig));

    CAMX_LOG_INFO(CamxLogGroupAEC,
                  "AEC: Publish startup exp in UseCase Pool: Gain=%f ExpTime=%llu StatsConfig HxV = %dx%d",
                  engineFrameControl.exposureData[0].linearGain,
                  engineFrameControl.exposureData[0].exposureTime,
                  usecaseStatsControl.statsConfig.BEConfig.horizontalNum,
                  usecaseStatsControl.statsConfig.BEConfig.verticalNum);

    m_engineStatsOutput.algorithmOutput.engineFrameControl = engineFrameControl;

    // Populate AEC update for frame control and stats control
    PopulatePropPoolFrameControl(&m_engineStatsOutput, &frameControl);
    PopulatePropPoolStatsControl(&m_engineStatsOutput, &statsControl);

    static const UINT WriteProps[] =
    {
        PropertyIDUsecaseAECFrameControl,
        PropertyIDUsecaseAECStatsControl
    };
    const VOID* pOutputData[CAMX_ARRAY_SIZE(WriteProps)] =
    {
        &usecaseFrameControl,
        &usecaseStatsControl
    };
    UINT pDataCount[CAMX_ARRAY_SIZE(WriteProps)] =
    {
        sizeof(usecaseFrameControl),
        sizeof(usecaseStatsControl),
    };

    // Writing only usecase, so can write outside EPR
    m_pNode->WriteDataList(WriteProps, pOutputData, pDataCount, CAMX_ARRAY_SIZE(WriteProps));

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CAECStatsProcessor::SetAlgoBayerGridValue
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetAlgoBayerGridValue(
    PropertyISPHDRBEStats*  pISPHDRStats,
    ParsedHDRBEStatsOutput* pHDRBEOutput,
    StatsBayerGrid*         pBayerGrid)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    CamxResult  result       = CamxResultSuccess;
    BGBEConfig* pHDRBEConfig = &pISPHDRStats->statsConfig.HDRBEConfig;

    pBayerGrid->horizontalRegionCount       = pHDRBEConfig->horizontalNum;
    pBayerGrid->verticalRegionCount         = pHDRBEConfig->verticalNum;
    pBayerGrid->totalRegionCount            = (pHDRBEConfig->horizontalNum * pHDRBEConfig->verticalNum);
    pBayerGrid->regionWidth                 = pISPHDRStats->statsConfig.regionWidth;
    pBayerGrid->regionHeight                = pISPHDRStats->statsConfig.regionHeight;
    pBayerGrid->bitDepth                    = IFEPipelineBitWidth;

    CAMX_STATIC_ASSERT(sizeof(pBayerGrid->satThreshold) == sizeof(pHDRBEConfig->channelGainThreshold));
    CamX::Utils::Memcpy(pBayerGrid->satThreshold,
                        pHDRBEConfig->channelGainThreshold,
                        sizeof(pBayerGrid->satThreshold));

    pBayerGrid->flags.hasSatInfo            = pHDRBEOutput->flags.hasSatInfo;
    pBayerGrid->flags.usesY                 = pHDRBEOutput->flags.usesY;
    pBayerGrid->numValidRegions             = pHDRBEOutput->numROIs;

    CAMX_STATIC_ASSERT(sizeof(*pBayerGrid->GetChannelData(0)) == sizeof(pHDRBEOutput->GetChannelData(0)));
    pBayerGrid->SetChannelDataArray(reinterpret_cast<StatsBayerGridChannelInfo*>(pHDRBEOutput->GetChannelDataArray()));

    CAMX_TRACE_MESSAGE_F(CamxLogGroupAEC, "numvalid %d, totalreg %d, h %d, v %d", pBayerGrid->numValidRegions,
        pBayerGrid->totalRegionCount, pBayerGrid->horizontalRegionCount, pBayerGrid->verticalRegionCount);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetAlgoBayerHDRBHistValue
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetAlgoBayerHDRBHistValue(
    ParsedHDRBHistStatsOutput*  pHDRBHistStatsOutput,
    StatsBayerHist*             pBayerHistogram)
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);

    CamxResult result = CamxResultSuccess;

    pBayerHistogram->channelCount = 3;
    pBayerHistogram->binCount     = pHDRBHistStatsOutput->numBins;

    if (pHDRBHistStatsOutput->numBins == 0)
    {
        CAMX_LOG_WARN(CamxLogGroupAEC, "0 BIN COUNT!");
        pHDRBHistStatsOutput->numBins = pHDRBHistStatsOutput->numBins;
    }

    pBayerHistogram->histDataType[0] = StatsColorChannelR;
    pBayerHistogram->pHistData[0]    = pHDRBHistStatsOutput->HDRBHistStats.redHistogram;

    pBayerHistogram->histDataType[1] = StatsColorChannelG;
    pBayerHistogram->pHistData[1]    = pHDRBHistStatsOutput->HDRBHistStats.greenHistogram;

    pBayerHistogram->histDataType[2] = StatsColorChannelB;
    pBayerHistogram->pHistData[2]    = pHDRBHistStatsOutput->HDRBHistStats.blueHistogram;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetAlgoBayerHistValue
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetAlgoBayerHistValue(
    ParsedBHistStatsOutput* pBHistStatsOutput,
    StatsBayerHist*         pBayerHistogram)
{
    CamxResult result = CamxResultSuccess;

    pBayerHistogram->binCount     = pBHistStatsOutput->numBins;

    // For Bhist at a time only channel is set always.
    pBayerHistogram->channelCount = 1;

    for (UINT i = 1; i < StatisticsBayerChannelsCount; i++)
    {
        pBayerHistogram->histDataType[i] = StatsColorChannelInvalid;
    }

    UINT8 channel = static_cast<UINT8>(pBHistStatsOutput->channelType);
    switch (channel)
    {
        case ColorChannelR:
            pBayerHistogram->histDataType[0] = StatsColorChannelR;
            break;
        case ColorChannelB:
            pBayerHistogram->histDataType[0] = StatsColorChannelB;
            break;
        case ColorChannelGR:
            pBayerHistogram->histDataType[0] = StatsColorChannelGR;
            break;
        case ColorChannelGB:
            pBayerHistogram->histDataType[0] = StatsColorChannelGB;
            break;
        case ColorChannelG:
            pBayerHistogram->histDataType[0] = StatsColorChannelG;
            break;
        case ColorChannelY:
            pBayerHistogram->histDataType[0] = StatsColorChannelY;
            break;
        default:
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Unsupported bayer hist channel!");
            result = CamxResultEUnsupported;
            break;
    }

    if (CamxResultSuccess == result)
    {
        pBayerHistogram->pHistData[0] = pBHistStatsOutput->BHistogramStats;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PrepareAlgorithmOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PrepareAlgorithmOutput(
    AECEngineProcessStatsOutput*  pOutput,
    UINT64                        requestId
    ) const
{
    CAMX_ASSERT(NULL != pOutput);

    if (m_pNode->GetStaticSettings()->maxPipelineDelay < requestId)
    {
        CamX::Utils::Memset(pOutput, 0, sizeof(AECEngineProcessStatsOutput));
    }
    pOutput->vendorTagList = m_algoVendorTagOutputList;

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PopulatePropPoolFrameControl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PopulatePropPoolFrameControl(
    AECEngineProcessStatsOutput* pOutput,
    AECFrameControl*             pFrameControlOut)
{
    CamxResult             result               = CamxResultSuccess;
    AECEngineFrameControl* pEngineFrameControl  = NULL;
    AECAlgoExposureData*   pExposureData        = NULL;

    CAMX_ASSERT(NULL != pOutput);
    CAMX_ASSERT(NULL != pFrameControlOut);

    pEngineFrameControl = &pOutput->algorithmOutput.engineFrameControl;
    pExposureData       = &pOutput->algorithmOutput.engineFrameControl.exposureData[0];

    CAMX_STATIC_ASSERT(ExposureIndexCount == AECAlgoExposureCount);

    pFrameControlOut->LEDInfluenceRatio        = pEngineFrameControl->frameControl.LEDInfluenceRatio;
    pFrameControlOut->luxIndex                 = pEngineFrameControl->frameControl.luxIndex;
    pFrameControlOut->preFlashState            = pOutput->preFlashState;
    pFrameControlOut->calibFlashState          = pOutput->calibFlashState;
    pFrameControlOut->flashInfo                = GetFlashInfoType(pEngineFrameControl->frameControl.flashState);
    pFrameControlOut->LEDCurrents[LEDSetting1] = pEngineFrameControl->LEDCurrents[LEDSetting1];
    pFrameControlOut->LEDCurrents[LEDSetting2] = pEngineFrameControl->LEDCurrents[LEDSetting2];
    pFrameControlOut->LEDFirstEntryRatio       = pEngineFrameControl->frameControl.LEDFirstEntryRatio;
    pFrameControlOut->LEDLastEntryRatio        = pEngineFrameControl->frameControl.LEDLastEntryRatio;

    for (UINT32 i = 0; i < ExposureIndexCount; i++)
    {
        if ((0.0f == pExposureData[i].linearGain) || (0 == pExposureData[i].exposureTime))
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Invalid exposure parameters: gain: %f, exposureTime: %llu",
                           pExposureData[i].linearGain, pExposureData[i].exposureTime);
            result = CamxResultEFailed;
            break;
        }

        pFrameControlOut->exposureInfo[i].deltaEVFromTarget = pExposureData[i].deltaEVFromTarget;
        pFrameControlOut->exposureInfo[i].linearGain        = pExposureData[i].linearGain;
        pFrameControlOut->exposureInfo[i].sensitivity       = pExposureData[i].sensitivity;
        pFrameControlOut->exposureInfo[i].exposureTime      = pExposureData[i].exposureTime;
    }

    UINT32       metadataAECFrameControl;
    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera2.statsconfigs", "AECFrameControl",
        &metadataAECFrameControl);
    if (CDKResultSuccess == result)
    {
        static const UINT PropertiesAECFrame[] = { metadataAECFrameControl };
        const static UINT length = CAMX_ARRAY_SIZE(PropertiesAECFrame);
        const VOID*       ppData[length] = { pFrameControlOut };
        UINT              pDataCount[length] = { sizeof(AECFrameControl) };

        FLOAT realGain = pFrameControlOut->exposureInfo[ExposureIndexSafe].linearGain;
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Stats Processor: AEC Gain published = %f", realGain);

        result = m_pNode->WriteDataList(PropertiesAECFrame, ppData, pDataCount, length);
        if (CDKResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "cannot write to AEC Frame control vendor tag");
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupAEC, "cannot query AEC Frame control vendor tag");
    }

    return result;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PopulatePropPoolPeerControl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::PopulatePropPoolPeerControl(
    AECEngineProcessStatsOutput* pOutput,
    VOID**             ppPeerInfo)
{
    *ppPeerInfo = pOutput->algorithmOutput.pPeerInfo;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PopulatePropPoolStatsControl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PopulatePropPoolStatsControl(
    AECEngineProcessStatsOutput* pOutput,
    AECStatsControl*             pStatsControl)
{
    CamxResult result = CamxResultSuccess;

    SetStatsConfigFromAlgoConfig(&(pOutput->algorithmOutput.engineFrameControl.statsConfig),
                                 &(pOutput->algorithmOutput.engineFrameControl.statsBHISTConfig),
                                 &(pStatsControl->statsConfig));

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PopulatePropPoolFrameInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PopulatePropPoolFrameInfo(
    AECEngineProcessStatsOutput* pOutput,
    AECFrameInformation*         pFrameInfo,
    AECEngineHALParam*           pHALParam)
{
    CamxResult           result            = CamxResultSuccess;
    AECAlgoFrameInfo*    pAlgoFrameInfo    = NULL;
    AECAlgoFrameControl* pAlgoFrameControl = NULL;
    UINT32*              pLEDCurrents      = NULL;
    AECAlgoExposureData* pExposureData     = NULL;

    pAlgoFrameInfo      = &pOutput->algorithmOutput.frameInfo;
    pAlgoFrameControl   = &pOutput->algorithmOutput.engineFrameControl.frameControl;
    pLEDCurrents        = &pOutput->algorithmOutput.engineFrameControl.LEDCurrents[0];
    pExposureData       = &pOutput->algorithmOutput.engineFrameControl.exposureData[0];

    /// @todo (CAMX-1171): Implement Pre-capture Trigger feature
    pFrameInfo->AECPrecaptureTrigger    = PrecapTriggerIdle;
    pFrameInfo->AECPreFlashState        = pOutput->preFlashState;
    pFrameInfo->AECSettled              = pAlgoFrameInfo->aecSettled;
    pFrameInfo->pCustomData             = NULL;
    /// @todo (CAMX-649): Implement debug data support
    pFrameInfo->pDebugData              = NULL;
    pFrameInfo->ISOValue                = pAlgoFrameControl->ISOValue;
    pFrameInfo->LEDAFRequired           = pAlgoFrameInfo->LEDAFRequired;
    pFrameInfo->luxIndex                = pAlgoFrameControl->luxIndex;
    pFrameInfo->frameLuma               = pAlgoFrameInfo->frameLuma;

    CAMX_STATIC_ASSERT(LEDSettingCount == StatisticsMaxNumOfLED);

    pFrameInfo->LEDCurrents[LEDSetting1]    = pLEDCurrents[LEDSetting1];
    pFrameInfo->LEDCurrents[LEDSetting2]    = pLEDCurrents[LEDSetting2];
    pFrameInfo->frameDuration               = pHALParam->frameDuration;

    CAMX_STATIC_ASSERT( ExposureIndexCount == AECAlgoExposureCount );

    for (UINT32 i = 0; i < ExposureIndexCount; i++)
    {
        if (pExposureData[i].linearGain == 0.0f || pExposureData[i].exposureTime == 0)
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Invalid exposure parameters: gain: %f, exposureTime: %llu",
                           pExposureData[i].linearGain, pExposureData[i].exposureTime);
            result = CamxResultEFailed;
            break;
        }

        pFrameInfo->exposureInfo[i].deltaEVFromTarget = pExposureData[i].deltaEVFromTarget;
        pFrameInfo->exposureInfo[i].linearGain        = pExposureData[i].linearGain;
        pFrameInfo->exposureInfo[i].sensitivity       = pExposureData[i].sensitivity;
        pFrameInfo->exposureInfo[i].exposureTime      = pExposureData[i].exposureTime;
    }

    if (CamxResultSuccess == result)
    {

        switch (pAlgoFrameInfo->snapshotIndicator)
        {
            case AECAlgoSnapshotNormal:
                pFrameInfo->snapshotIndicator = SnapshotTypeNormal;
                break;

            case AECAlgoSnapshotFlash:
                pFrameInfo->snapshotIndicator = SnapshotTypeFlash;
                break;

            case AECAlgoSnapshotLLS:
                pFrameInfo->snapshotIndicator = SnapshotTypeLLS;
                break;

            default:
                CAMX_LOG_ERROR(CamxLogGroupAEC, "Invalid snapshot indicator: %d", pAlgoFrameInfo->snapshotIndicator);
                pFrameInfo->snapshotIndicator = SnapshotTypeNormal;
                break;
        }

        switch (pAlgoFrameInfo->touchEVIndicator)
        {
            case AECAlgoTouchEVInactive:
                pFrameInfo->touchEVIndicator = AECTouchEVTypeInactive;
                break;

            case AECAlgoTouchEVConverging:
                pFrameInfo->touchEVIndicator = AECTouchEVTypeConverging;
                break;

            case AECAlgoTouchEVSettled:
                pFrameInfo->touchEVIndicator = AECTouchEVTypeSettled;
                break;

            case AECAlgoTouchEVSceneChange:
                pFrameInfo->touchEVIndicator = AECTouchEVTypeSceneChange;
                break;

            default:
                CAMX_LOG_ERROR(CamxLogGroupAEC, "Invalid EV indicator type: %d",
                               pAlgoFrameInfo->touchEVIndicator);
                pFrameInfo->touchEVIndicator = AECTouchEVTypeInactive;
                break;
        }
    }

    if (CamxResultSuccess == result)
    {
        /* Update the fields required for EXIF update */
        pFrameInfo->meteringMode        = static_cast<UINT16>(pHALParam->AEMeteringMode);
        pFrameInfo->exposureProgram     = (AECState::Manual == m_pAECEngine->GetAECState()) ?
                                          AECExposureProgramManual : AECExposureProgramNormal;
        pFrameInfo->brightnessValue     = pOutput->algorithmOutput.engineFrameControl.apexData.brightnessValue;
        pFrameInfo->sceneType           = 1; // If a DSC recorded the image, this tag value shall always be set to 1
        if (ControlAEModeOff == pHALParam->AEMode)
        {
            pFrameInfo->exposureMode = 1; // 1 = Manual exposure
        }
        else
        {
            pFrameInfo->exposureMode = 0; // 0 = Auto exposure
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::PopulatePropPoolInternal
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::PopulatePropPoolInternal(
    AECEngineProcessStatsOutput* pOutput,
    AECOutputInternal*           pInternalOutput)
{
    AECAlgoFrameInfo*    pAlgoFrameInfo         = &pOutput->algorithmOutput.frameInfo;
    AECAlgoFrameControl* pAlgoFrameControl      = &pOutput->algorithmOutput.engineFrameControl.frameControl;
    AECAlgoAPEXData*     pAPEXData              = &pOutput->algorithmOutput.engineFrameControl.apexData;

    pInternalOutput->APEXValues.brightness      = pAPEXData->brightnessValue;
    pInternalOutput->APEXValues.aperture        = pAPEXData->apertureValue;
    pInternalOutput->APEXValues.speed           = pAPEXData->speedValue;
    pInternalOutput->APEXValues.time            = pAPEXData->timeValue;
    pInternalOutput->APEXValues.exposure        = pAPEXData->exposureValue;
    pInternalOutput->asdExtremeGreenRatio       = pAlgoFrameInfo->asdExtremeGreenRatio;
    pInternalOutput->asdExtremeBlueRatio        = pAlgoFrameInfo->asdExtremeBlueRatio;
    pInternalOutput->brightnessSettled          = pAlgoFrameInfo->brightnessSettled;

    /* AWB Needs Main flash gains at start of AWB, rather than exactly at Main flash time frame */
    if (PreFlashStateTriggerAWB == pOutput->preFlashState)
    {
        pAlgoFrameControl                   = &pOutput->algorithmOutput.engineFrameControl.mainFlashFrameControl;
    }

    pInternalOutput->flashInfo              = GetFlashInfoType(pAlgoFrameControl->flashState);;
    pInternalOutput->LEDInfluenceRatio      = pAlgoFrameControl->LEDInfluenceRatio;
    pInternalOutput->LEDRGRatio             = pAlgoFrameControl->LEDRGRatio;
    pInternalOutput->LEDBGRatio             = pAlgoFrameControl->LEDBGRatio;

    CAMX_STATIC_ASSERT(YStatsNum == StatisticsYStatsNum);
    CAMX_STATIC_ASSERT(sizeof(pInternalOutput->legacyYStats) == sizeof(pAlgoFrameInfo->legacyYStats));
    CamX::Utils::Memcpy(&pInternalOutput->legacyYStats[0],
                        &pAlgoFrameInfo->legacyYStats[0],
                        sizeof(pInternalOutput->legacyYStats));

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::ReadHALAECParam
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::ReadHALAECParam(
    AECEngineHALParam* pHALParam,
    const StatsProcessRequestData* pStatsProcessRequestDataInfo
    ) const
{
    CamxResult            result          = CamxResultSuccess;
    const StaticSettings* pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();

    CAMX_ASSERT(NULL != pHALParam);

    UINT32      metaTagFDROI;
    CamxResult  resultFDROI;

#define ENTRY(sec, tag, val) \
  { 0, CamxResultSuccess, tag, sec, &val, sizeof(val) }

    AECVendorTagList vendorTags[] =
    {
        ENTRY("org.codeaurora.qcamera3.exposure_metering", "exposure_metering_mode", pHALParam->AEMeteringMode),
        ENTRY("org.codeaurora.qcamera3.iso_exp_priority", "select_priority", pHALParam->ISOExposureTimePriortyMode),
        ENTRY("org.codeaurora.qcamera3.iso_exp_priority", "use_iso_exp_priority", pHALParam->ISOorExposureTimePriorityValue),
        ENTRY("org.codeaurora.qcamera3.iso_exp_priority", "use_iso_value", pHALParam->ISOValue ),
        ENTRY("org.codeaurora.qcamera3.ae_bracket", "mode", pHALParam->AEBracketMode),
    };
#undef ENTRY

    static const UINT VendorDataLength        = CAMX_ARRAY_SIZE(vendorTags);
    VOID* pVendorData[VendorDataLength]       = { 0 };
    UINT64 vendorDataOffset[VendorDataLength] = { 0 };
    UINT vendorData[VendorDataLength];

    for (UINT i = 0; i < VendorDataLength; i++)
    {

        vendorTags[i].result = VendorTagManager::QueryVendorTagLocation(vendorTags[i].pSectionName,
            vendorTags[i].pTagName,
            &vendorTags[i].metaTag);

        if (vendorTags[i].result == CamxResultSuccess)
        {
            vendorTags[i].metaTag |= InputMetadataSectionMask;
        }
        // Static Array have to getdatalist even vendor tag does ot exist
        vendorData[i] = vendorTags[i].metaTag;
    }

    if (TRUE == m_pNode->GetStaticSettings()->useFDUseCasePool)
    {
        metaTagFDROI = PropertyIDUsecaseFDResults;
    }
    else
    {
        resultFDROI = VendorTagManager::QueryVendorTagLocation(VendorTagSectionOEMFDResults,
            VendorTagNameOEMFDResults,
            &metaTagFDROI);
    }

    UINT HALAECData[] =
    {
        InputControlAEExposureCompensation,                 // 0
        InputControlAELock,                                 // 1
        InputControlAEMode,                                 // 2
        InputControlAEPrecaptureTrigger,                    // 3
        InputControlAFTrigger,                              // 4
        InputControlCaptureIntent,                          // 5
        InputControlMode,                                   // 6
        InputControlSceneMode,                              // 7
        InputFlashMode,                                     // 8
        InputSensorExposureTime,                            // 9
        InputSensorSensitivity,                             // 10
        InputControlAEAntibandingMode,                      // 11
        InputControlAERegions,                              // 12
        metaTagFDROI,                                       // 13
        FlashState,                                         // 14
        InputControlAETargetFpsRange,                       // 15
        InputControlZslEnable,                              // 16
        SensorFrameDuration                                 // 17
    };

    static const UINT HALAECDataLength = CAMX_ARRAY_SIZE(HALAECData);
    VOID* pData[HALAECDataLength] = { 0 };
    UINT64 HALAECDataOffset[HALAECDataLength] = { 0 };

    HALAECDataOffset[13] = m_pNode->GetStaticSettings()->maxPipelineDelay;
    HALAECDataOffset[17] = m_pNode->GetStaticSettings()->maxPipelineDelay;

    m_pNode->GetDataList(HALAECData, pData, HALAECDataOffset, HALAECDataLength);

    Utils::Memcpy(&pHALParam->AECompensation, (pData[0]), sizeof(pHALParam->AECompensation));
    Utils::Memcpy(&pHALParam->AELock, (pData[1]), sizeof(pHALParam->AELock));
    Utils::Memcpy(&pHALParam->AEMode, (pData[2]), sizeof(pHALParam->AEMode));
    if ((StatsAlgoRoleDefault == pStatsProcessRequestDataInfo->cameraInfo.algoRole) ||
        (StatsAlgoRoleMaster == pStatsProcessRequestDataInfo->cameraInfo.algoRole))
    {
        Utils::Memcpy(&pHALParam->AETrigger, (pData[3]), sizeof(pHALParam->AETrigger));
    }
    else
    {
        pHALParam->AETrigger = ControlAEPrecaptureTriggerIdle;
    }

    Utils::Memcpy(&pHALParam->AFTrigger, (pData[4]), sizeof(pHALParam->AFTrigger));
    Utils::Memcpy(&pHALParam->captureIntent, (pData[5]), sizeof(pHALParam->captureIntent));
    Utils::Memcpy(&pHALParam->controlMode, (pData[6]), sizeof(pHALParam->controlMode));
    Utils::Memcpy(&pHALParam->controlSceneMode, (pData[7]), sizeof(pHALParam->controlSceneMode));
    Utils::Memcpy(&pHALParam->flashMode, (pData[8]), sizeof(pHALParam->flashMode));
    Utils::Memcpy(&pHALParam->exposureTime, (pData[9]), sizeof(pHALParam->exposureTime));
    Utils::Memcpy(&pHALParam->sensitivity, (pData[10]), sizeof(pHALParam->sensitivity));
    Utils::Memcpy(&pHALParam->AEAntibandingModeValue, (pData[11]), sizeof(pHALParam->AEAntibandingModeValue));

    if (NULL != pData[12])
    {
        SetTouchROISettings(pHALParam, reinterpret_cast<WeightedRectangle*>(pData[12]));
    }
    if (NULL != pData[13])
    {
        SetFaceROISettings(pHALParam, reinterpret_cast<FaceROIInformation*>(pData[13]));
    }

    // Read the Antibanding mode published by AFD if the Antibanding Mode is set to AUTO.
    ReadAFDMode(pHALParam);
    pHALParam->sensorFlashState = FlashStateUnavailable;

    if (NULL != pData[14])
    {
        Utils::Memcpy(&pHALParam->sensorFlashState, (pData[14]), sizeof(pHALParam->sensorFlashState));
    }

    if (NULL != pData[15])
    {
        Utils::Memcpy(&pHALParam->FPSRange, (pData[15]), sizeof(pHALParam->FPSRange));
    }

    if (NULL != pData[16])
    {
        Utils::Memcpy(&pHALParam->controlZslEnable, (pData[16]), sizeof(pHALParam->controlZslEnable));
    }
    if (NULL != pData[17])
    {
        Utils::Memcpy(&pHALParam->frameDuration, (pData[17]), sizeof(pHALParam->frameDuration));
    }

    m_pNode->GetDataList(vendorData, pVendorData, vendorDataOffset, VendorDataLength);

    for (UINT i = 0; i < CAMX_ARRAY_SIZE(vendorTags); i++)
    {
        if (CamxResultSuccess == vendorTags[i].result  && NULL != pVendorData[i])
        {
            Utils::Memcpy(vendorTags[i].pData, pVendorData[i], vendorTags[i].size);
        }
    }

    SetISOExpPriorityValue(pHALParam);
    // This needs to be called at last as it overrides AEC metering, compensation and ISO values based on scene mode
    SetBestShotModeSettings(pHALParam);

    if (1 == pStaticSettings->feature1)
    {
        pHALParam->isVideoHDR = TRUE;
    }

    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "ReqId:%llu, camID:%d, AECompensation:%d, AELock:%d, AEMode:%d, AEMeteringMode:%d, "
                     "AETrigger:%d, AFTrigger:%d, captureIntent:%d, controlMode:%d, controlSceneMode:%d, flashMode:%d, "
                     "exposureTime:%lld, sensitivity:%d, AEAntibandingModeValue:%d, ISOExpTimePrioritySet:%d, "
                     "ISOorExposureTimePriorityValue:%lld, ISOValue:%d, FPS Range:(min:%d max:%d), AEBracketMode:%d, "
                     "isVideoHDR:%d, ZSLEnable %d, FrameDuration:%lld Face (Cnt:%d x:%f y:%f dx:%f dy:%f), "
                     "Touch (x:%f y:%f dx:%f dy:%f)",
                     m_currProcessingRequestId,
                     m_cameraId,
                     pHALParam->AECompensation,
                     pHALParam->AELock,
                     pHALParam->AEMode,
                     pHALParam->AEMeteringMode,
                     pHALParam->AETrigger,
                     pHALParam->AFTrigger,
                     pHALParam->captureIntent,
                     pHALParam->controlMode,
                     pHALParam->controlSceneMode,
                     pHALParam->flashMode,
                     pHALParam->exposureTime,
                     pHALParam->sensitivity,
                     pHALParam->AEAntibandingModeValue,
                     pHALParam->ISOExposureTimePriortyMode,
                     pHALParam->ISOorExposureTimePriorityValue,
                     pHALParam->ISOValue,
                     pHALParam->FPSRange.min,
                     pHALParam->FPSRange.max,
                     pHALParam->AEBracketMode,
                     pHALParam->isVideoHDR,
                     pHALParam->controlZslEnable,
                     pHALParam->frameDuration,
                     pHALParam->faceROICount,
                     pHALParam->faceROI[0].x,
                     pHALParam->faceROI[0].y,
                     pHALParam->faceROI[0].dx,
                     pHALParam->faceROI[0].dx,
                     pHALParam->touchROI.x,
                     pHALParam->touchROI.y,
                     pHALParam->touchROI.dx,
                     pHALParam->touchROI.dy);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetHardwareInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetHardwareInfo(
    AECEngineHWInfo*    pHardwareInfo,
    HwContext*          pHardwareContext,
    INT32               cameraID
    ) const
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
    CAMX_ASSERT(NULL != pHardwareInfo);

    CamxResult                      result              = CamxResultSuccess;
    StatsSensorInfo*                pSensorInfo         = &pHardwareInfo->sensorInfo;
    StatsCapability*                pStatsCapabilities  = &pHardwareInfo->statsCapabilities;
    StatsRectangle                  activeWindow        = { 0 };
    SensorModuleStaticCaps          sensorStaticCaps    = { 0 };
    const ImageSensorModuleData*    pSensorModuleData   = pHardwareContext->GetImageSensorModuleData(cameraID);

    CAMX_ASSERT(NULL != pSensorModuleData && NULL != pSensorModuleData->GetSensorDataObject());
    pSensorModuleData->GetSensorDataObject()->GetSensorStaticCapability(&sensorStaticCaps, cameraID);

    pSensorInfo->sensorActiveResWidth  = static_cast<UINT32>(sensorStaticCaps.activeArraySize.width);
    pSensorInfo->sensorActiveResHeight =  static_cast<UINT32>(sensorStaticCaps.activeArraySize.height);

    if ((CSLPresilEnabled == GetCSLMode()) || (CSLPresilRUMIEnabled == GetCSLMode()))
    {
        const StaticSettings* pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();

        pSensorInfo->currentFPS             = Utils::FloatToQNumber(30, 256);
        pSensorInfo->maxFPS                 = Utils::FloatToQNumber(30, 256);
        pSensorInfo->currentLinesPerFrame   = 1;
        pSensorInfo->currentMaxLineCount    = 1;
        pSensorInfo->maxGain                = 1.0f;
        pSensorInfo->pixelClock             = 1;
        pSensorInfo->pixelClockPerLine      = 1;
        pSensorInfo->sensorCapabilities     = 0;
        pSensorInfo->sensorResWidth         = pStaticSettings->IFETestImageSizeWidth;
        pSensorInfo->sensorResHeight        = pStaticSettings->IFETestImageSizeHeight;
        pSensorInfo->pixelSumFactor         = 1;
        pSensorInfo->fNumber                = 1.0f;

        activeWindow.top    = 0;
        activeWindow.left   = 0;
        activeWindow.width  = static_cast<INT32>(pSensorInfo->sensorResWidth);
        activeWindow.height = static_cast<INT32>(pSensorInfo->sensorResHeight);
        result = CamxResultSuccess;
    }
    else
    {
        const SensorMode* pSensorMode = NULL;
        result = m_pNode->GetSensorModeData(&pSensorMode);
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Failed to get the sensor mode!");
        }
        else
        {
            /// @todo  (CAMX-1245): Sensor to publish current FPS to the property pool.
            ///                     Below information on current FPS is incorrect
            pSensorInfo->currentFPS             = Utils::FloatToQNumber(30, 256);
            pSensorInfo->maxFPS                 = Utils::DoubleToQNumber(pSensorMode->maxFPS, 256);
            pSensorInfo->currentLinesPerFrame   = static_cast<UINT32>(pSensorMode->numLinesPerFrame);
            pSensorInfo->currentMaxLineCount    = static_cast<UINT32>(pSensorMode->maxLineCount);
            pSensorInfo->maxGain                = static_cast<FLOAT>( pSensorMode->maxGain);
            pSensorInfo->pixelClock             = static_cast<UINT32>(pSensorMode->vtPixelClock);
            pSensorInfo->pixelClockPerLine      = static_cast<UINT32>(pSensorMode->numPixelsPerLine);
            /// @todo (CAMX-2264): Sensor capabilities missing
            pSensorInfo->sensorCapabilities     = 0;

            /// @todo (CAMX-2184): use binningTypeH vs binnignTypeV
            pSensorInfo->pixelSumFactor         = static_cast<UINT16>(pSensorMode->binningTypeH);
            /// @todo (CAMX-2264): Sensor F-number missing
            pSensorInfo->fNumber                = 1.0f;

            // Active sensor region for which stats is collected is the cropped sensor output
            const IFEInputResolution* pIFEInput = NULL;
            result = m_pNode->GetIFEInputResolution(&pIFEInput);
            if ((CamxResultSuccess != result) || (NULL == pIFEInput))
            {
                pSensorInfo->sensorResWidth         = static_cast<UINT32>(pSensorMode->resolution.outputWidth);
                pSensorInfo->sensorResHeight        = static_cast<UINT32>(pSensorMode->resolution.outputHeight);
                activeWindow.top    = static_cast<INT32>(pSensorMode->cropInfo.firstLine);
                activeWindow.left   = static_cast<INT32>(pSensorMode->cropInfo.firstPixel);
                activeWindow.width  = static_cast<INT32>(pSensorMode->cropInfo.lastPixel -
                    pSensorMode->cropInfo.firstPixel + 1);
                activeWindow.height = static_cast<INT32>(pSensorMode->cropInfo.lastLine  -
                    pSensorMode->cropInfo.firstLine  + 1);
            }
            else
            {
                pSensorInfo->sensorResWidth         = static_cast<UINT32>(pIFEInput->resolution.width);
                pSensorInfo->sensorResHeight        = static_cast<UINT32>(pIFEInput->resolution.height);
                activeWindow.top    =  static_cast<INT32>(pIFEInput->CAMIFWindow.top);
                activeWindow.left   =  static_cast<INT32>(pIFEInput->CAMIFWindow.left);
                activeWindow.width  =  static_cast<INT32>(pIFEInput->CAMIFWindow.width);
                activeWindow.height =  static_cast<INT32>(pIFEInput->CAMIFWindow.height);
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        pStatsCapabilities->activePixelWindow        = activeWindow;
        pStatsCapabilities->maxBitDepth              = BGStatsMaximumBitWidth;
        // Currently none of the additional HW capabilities are supported
        pStatsCapabilities->HWStatsCapabilities      = 0;
        pStatsCapabilities->maxHWStatsSkippingFactor = 0;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetPeerInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetPeerInfo(
    const StatsProcessRequestData* pStatsProcessRequestDataInfo,
    VOID** ppPeerInfo
    ) const
{
    CamxResult        result = CamxResultSuccess;
    INT64             requestDelta = 0;
    UINT64            peerRequestID = pStatsProcessRequestDataInfo->pMultiRequestSync->requestID[m_peerpipelineId];
    if (TRUE == pStatsProcessRequestDataInfo->pMultiRequestSync->isMultiRequest)
    {
        if (StatsAlgoProcessMapping == pStatsProcessRequestDataInfo->algoAction)
        {
            // Slave/Follower
            requestDelta = m_currProcessingRequestId - peerRequestID;
        }
        else if (StatsAlgoProcessRequest == pStatsProcessRequestDataInfo->algoAction)
        {
            // Master, will depend on previous slave
            requestDelta = m_currProcessingRequestId - peerRequestID + 1;
        }
    }

    // Get property from peer pipeline
    static const UINT GetProps[]              = { PropertyIDAECPeerInfo };
    static const UINT GetPropsLength          = CAMX_ARRAY_SIZE(GetProps);
    VOID*             pData[GetPropsLength]   = { 0 };
    UINT64            offsets[GetPropsLength] = { static_cast<UINT64>(abs(requestDelta)) };
    BOOL              negates[GetPropsLength] = { (requestDelta < 0) ? TRUE : FALSE };

    result = m_pNode->GetDataListFromPipeline(GetProps, pData, offsets, GetPropsLength, negates, m_peerpipelineId);
    if (NULL != pData[0])
    {
        *ppPeerInfo = *(reinterpret_cast<VOID**>(pData[0]));
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "MultiCamera Pdata:!null PeerInfo:%010p peer_pipeline_id:%d "
            "ReqId:%04llu PeerReqId:%llu CamId:%d negate:%d delta:%d algoAction:%d isMultiRequest:%d",
            *ppPeerInfo, m_peerpipelineId, m_currProcessingRequestId,
            peerRequestID, m_cameraId, negates[0], requestDelta,
            pStatsProcessRequestDataInfo->algoAction,
            pStatsProcessRequestDataInfo->pMultiRequestSync->isMultiRequest);
    }
    else
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "MultiCamera Pdata:null PeerInfo:%010p peer_pipeline_id:%d "
            "ReqId:%04llu PeerReqId:%llu CamId:%d negate:%d delta:%d algoAction:%d isMultiRequest:%d",
            *ppPeerInfo, m_peerpipelineId, m_currProcessingRequestId,
            peerRequestID, m_cameraId, negates[0], requestDelta,
            pStatsProcessRequestDataInfo->algoAction,
            pStatsProcessRequestDataInfo->pMultiRequestSync->isMultiRequest);
    }
    return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetCropWindow
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::GetCropWindow(
    StatsRectangle* pCropWindow
    ) const
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
    CAMX_ASSERT(NULL != pCropWindow);

    const StaticSettings* pStaticSettings         = HwEnvironment::GetInstance()->GetStaticSettings();
    static const UINT     GetProps[]              = { InputScalerCropRegion };
    static const UINT     GetPropsLength          = CAMX_ARRAY_SIZE(GetProps);
    VOID*                 pData[GetPropsLength]   = { 0 };
    UINT64                offsets[GetPropsLength] = { 0 };
    CropWindow            HALCropWindow           = { 0 };
    CamxResult            result                  = CamxResultSuccess;
    FLOAT                 widthRatio              = static_cast<FLOAT>(m_hardwareInfo.sensorInfo.sensorResWidth) /
                                                                      (m_hardwareInfo.sensorInfo.sensorActiveResWidth);
    FLOAT                 heightRatio             = static_cast<FLOAT>(m_hardwareInfo.sensorInfo.sensorResHeight) /
                                                                      (m_hardwareInfo.sensorInfo.sensorActiveResHeight);
    CAMX_ASSERT(pStaticSettings != NULL);

    m_pNode->GetDataList(GetProps, pData, offsets, GetPropsLength);

    if (NULL != pData[0])
    {
        HALCropWindow = (*reinterpret_cast<CropWindow*>(pData[0]));
    }

    if (HALCropWindow.width > m_hardwareInfo.sensorInfo.sensorActiveResWidth)
    {
        CAMX_LOG_WARN(CamxLogGroupAEC, "Wrong input: HALCropWindow width(%d) > Active Array width(%d)",
            HALCropWindow.width, m_hardwareInfo.sensorInfo.sensorActiveResWidth);
        HALCropWindow.width = m_hardwareInfo.sensorInfo.sensorActiveResWidth;
    }
    if (HALCropWindow.height > m_hardwareInfo.sensorInfo.sensorActiveResHeight)
    {
        CAMX_LOG_WARN(CamxLogGroupAEC, "Wrong input: HALCropWindow height(%d) > Active Array height(%d)",
            HALCropWindow.height, m_hardwareInfo.sensorInfo.sensorActiveResHeight);
        HALCropWindow.height = m_hardwareInfo.sensorInfo.sensorActiveResHeight;
    }

    if (HALCropWindow.width  != 0 &&
        HALCropWindow.height != 0)
    {
        // mapping crop window to CAMIF size from Sensor Active pixel size
        pCropWindow->width  = Utils::RoundFLOAT(HALCropWindow.width * widthRatio);
        pCropWindow->height = Utils::RoundFLOAT(HALCropWindow.height * heightRatio);
        pCropWindow->left   = Utils::RoundFLOAT(HALCropWindow.left * widthRatio);
        pCropWindow->top    = Utils::RoundFLOAT(HALCropWindow.top * heightRatio);
    }
    else
    {
        // If the crop window has not been set, set it to the entire sensor crop window
        (*pCropWindow) = m_hardwareInfo.statsCapabilities.activePixelWindow;
        CAMX_LOG_WARN(CamxLogGroupAEC, "Wrong input: HALCropWindow width,height = %dx%d",
            HALCropWindow.width, HALCropWindow.height);
    }

    // Boundary check for Width
    if (m_hardwareInfo.sensorInfo.sensorResWidth < static_cast<UINT32>(pCropWindow->width + pCropWindow->left))
    {
        pCropWindow->width = m_hardwareInfo.sensorInfo.sensorResWidth - pCropWindow->left;
    }

    // Boundary check for Height
    if (m_hardwareInfo.sensorInfo.sensorResHeight < static_cast<UINT32>(pCropWindow->height + pCropWindow->top))
    {
        pCropWindow->height = m_hardwareInfo.sensorInfo.sensorResHeight - pCropWindow->top;
    }

    CAMX_LOG_VERBOSE(CamxLogGroupAEC,
        "Stats crop window:CamId:%d HALCrop %d %d %d %d, AdjustCrop %d %d %d %d, SensorRes %d %d, "
        "ActiveArray %d %d, w h ratio %f %f",
        m_cameraId,
        HALCropWindow.left,
        HALCropWindow.top,
        HALCropWindow.width,
        HALCropWindow.height,
        pCropWindow->left,
        pCropWindow->top,
        pCropWindow->width,
        pCropWindow->height,
        m_hardwareInfo.sensorInfo.sensorResWidth,
        m_hardwareInfo.sensorInfo.sensorResHeight,
        m_hardwareInfo.sensorInfo.sensorActiveResWidth,
        m_hardwareInfo.sensorInfo.sensorActiveResHeight,
        widthRatio,
        heightRatio);

    return result;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::UpdateTraceEvents
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::UpdateTraceEvents(
    AECEngineProcessStatsOutput* pProcessStatsOutput)
{
    // Convergence starts always for first frame or another other stream resets
    if (AECSettleUnknown == m_lastSettledState)
    {
        CAMX_TRACE_ASYNC_BEGIN_F(CamxLogGroupAEC, 0, "AEC: Convergence");
        m_lastSettledState = AECSettleFalse;
    }
    else
    {
        if (m_lastSettledState != pProcessStatsOutput->algorithmOutput.frameInfo.aecSettled)
        {
            if (FALSE == pProcessStatsOutput->algorithmOutput.frameInfo.aecSettled)
            {
                CAMX_TRACE_ASYNC_BEGIN_F(CamxLogGroupAEC, 0, "AEC: Convergence");
                m_lastSettledState = AECSettleFalse;
            }
            else
            {
                CAMX_TRACE_ASYNC_END_F(CamxLogGroupAEC, 0, "AEC: Convergence");
                m_lastSettledState = AECSettled;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetStatsConfigFromAlgoConfig
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::SetStatsConfigFromAlgoConfig(
    StatsBayerGridBayerExposureConfig*  pAlgoStatsConfig,
    StatsBayerHistogramConfig*          pAlgoBHISTStatsConfig,
    AECConfig*                          pStatsConfig
    ) const
{
    CAMX_ASSERT(NULL != pAlgoStatsConfig);
    CAMX_ASSERT(NULL != pStatsConfig);
    StatsRectangle statsROI = { 0 };

    // BG stats configuration
    pStatsConfig->BGConfig.channelGainThreshold[ChannelIndexGR] = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelGR];
    pStatsConfig->BGConfig.channelGainThreshold[ChannelIndexGB] = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelGB];
    pStatsConfig->BGConfig.channelGainThreshold[ChannelIndexR]  = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelR];
    pStatsConfig->BGConfig.channelGainThreshold[ChannelIndexB]  = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelB];
    pStatsConfig->BGConfig.horizontalNum                        = pAlgoStatsConfig->horizontalRegionCount;
    pStatsConfig->BGConfig.verticalNum                          = pAlgoStatsConfig->verticalRegionCount;
    pStatsConfig->BGConfig.outputBitDepth                       = pAlgoStatsConfig->outputBitDepth;
    CAMX_STATIC_ASSERT( sizeof(pAlgoStatsConfig->YStatsWeights) == sizeof(pStatsConfig->BGConfig.YStatsWeights));
    CAMX_ASSERT_MESSAGE(MaxAWBBGStatsNum >= (pStatsConfig->BGConfig.horizontalNum * pStatsConfig->BGConfig.verticalNum),
                        "AWBBGStatsNum out of bound");
    for (UINT32 i = 0; i < CAMX_ARRAY_SIZE(pAlgoStatsConfig->YStatsWeights); i++)
    {
        pStatsConfig->BGConfig.YStatsWeights[i] = pAlgoStatsConfig->YStatsWeights[i];
    }

    // BE stats configuration
    pStatsConfig->BEConfig.channelGainThreshold[ChannelIndexGR] = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelGR];
    pStatsConfig->BEConfig.channelGainThreshold[ChannelIndexGB] = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelGB];
    pStatsConfig->BEConfig.channelGainThreshold[ChannelIndexR]  = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelR];
    pStatsConfig->BEConfig.channelGainThreshold[ChannelIndexB]  = pAlgoStatsConfig->channelGainThreshold[StatsColorChannelB];
    pStatsConfig->BEConfig.horizontalNum                        = pAlgoStatsConfig->horizontalRegionCount;
    pStatsConfig->BEConfig.verticalNum                          = pAlgoStatsConfig->verticalRegionCount;
    pStatsConfig->BEConfig.outputBitDepth                       = pAlgoStatsConfig->outputBitDepth;
    CAMX_STATIC_ASSERT( sizeof(pAlgoStatsConfig->YStatsWeights) == sizeof(pStatsConfig->BEConfig.YStatsWeights));
    for (UINT32 i = 0; i < CAMX_ARRAY_SIZE(pAlgoStatsConfig->YStatsWeights); i++)
    {
        pStatsConfig->BEConfig.YStatsWeights[i] = pAlgoStatsConfig->YStatsWeights[i];
    }

    if (pAlgoStatsConfig->enableSaturationStats)
    {
        pStatsConfig->BGConfig.outputMode                       = BGBESaturationEnabled;
        pStatsConfig->BEConfig.outputMode                       = BGBESaturationEnabled;
    }
    else if (pAlgoStatsConfig->enableYStatsComputation)
    {
        pStatsConfig->BGConfig.outputMode                       = BGBEYStatsEnabled;
        pStatsConfig->BGConfig.greenType                        = GAverage;
        pStatsConfig->BEConfig.outputMode                       = BGBEYStatsEnabled;
        pStatsConfig->BGConfig.greenType                        = GAverage;
    }
    else
    {
        pStatsConfig->BGConfig.outputMode                       = BGBERegular;
        pStatsConfig->BEConfig.outputMode                       = BGBERegular;
    }

    // BHist stats configuration
    pStatsConfig->BHistConfig.channel                           = pAlgoBHISTStatsConfig->channel;
    pStatsConfig->BHistConfig.uniform                           = pAlgoBHISTStatsConfig->uniform;
    pStatsConfig->HDRBHistConfig.greenChannelInput              = HDRBHistSelectGR;

    // Configure the ROI selection
    switch (pAlgoStatsConfig->ROISelection)
    {
        case StatsROISelectionCustom:
            statsROI = pAlgoStatsConfig->ROI;
            break;

        case StatsROISelectionCroppedFOV:
            GetCropWindow(&statsROI);
            break;

        case StatsROISelectionFullFOV:
        default:
            statsROI = m_hardwareInfo.statsCapabilities.activePixelWindow;
            break;
    }

    // Set the stats ROI
    pStatsConfig->BGConfig.ROI.left         = statsROI.left;
    pStatsConfig->BGConfig.ROI.top          = statsROI.top;
    pStatsConfig->BGConfig.ROI.width        = statsROI.width;
    pStatsConfig->BGConfig.ROI.height       = statsROI.height;

    pStatsConfig->BEConfig.ROI.left         = statsROI.left;
    pStatsConfig->BEConfig.ROI.top          = statsROI.top;
    pStatsConfig->BEConfig.ROI.width        = statsROI.width;
    pStatsConfig->BEConfig.ROI.height       = statsROI.height;

    // Update ROI if Algo gave custom BHIST
    if (pAlgoBHISTStatsConfig->ROISelection == StatsROISelectionCustom)
    {
        statsROI = pAlgoBHISTStatsConfig->ROI;
    }
    pStatsConfig->BHistConfig.ROI.left      = statsROI.left;
    pStatsConfig->BHistConfig.ROI.top       = statsROI.top;
    pStatsConfig->BHistConfig.ROI.width     = statsROI.width;
    pStatsConfig->BHistConfig.ROI.height    = statsROI.height;

    pStatsConfig->HDRBHistConfig.ROI.left   = statsROI.left;
    pStatsConfig->HDRBHistConfig.ROI.top    = statsROI.top;
    pStatsConfig->HDRBHistConfig.ROI.width  = statsROI.width;
    pStatsConfig->HDRBHistConfig.ROI.height = statsROI.height;

    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "BG: hnum: %d vnum: %d, BE: hnum: %d, vnum: %d ROI(l:%d t:%d w:%d h:%d)"
        "BHIST: chnl:%d uniform:%d ROISel:%d ROI(l:%d t:%d w:%d h:%d)",
        pStatsConfig->BGConfig.horizontalNum, pStatsConfig->BGConfig.verticalNum,
        pStatsConfig->BEConfig.horizontalNum, pStatsConfig->BEConfig.verticalNum,
        pStatsConfig->BGConfig.ROI.left, pStatsConfig->BGConfig.ROI.top,
        pStatsConfig->BGConfig.ROI.width, pStatsConfig->BGConfig.ROI.height,
        pStatsConfig->BHistConfig.channel,
        pStatsConfig->BHistConfig.uniform,
        pAlgoBHISTStatsConfig->ROISelection,
        pStatsConfig->HDRBHistConfig.ROI.left, pStatsConfig->HDRBHistConfig.ROI.top,
        pStatsConfig->HDRBHistConfig.ROI.width, pStatsConfig->HDRBHistConfig.ROI.height);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetTouchROISettings
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::SetTouchROISettings(
    AECEngineHALParam* pHALParam,
    WeightedRectangle* pTouchROIInfo
    ) const
{
    // Taking local variable to save multiple derefrence and type cast.
    FLOAT          xMax         = static_cast<FLOAT>(pTouchROIInfo->xMax);
    FLOAT          yMax         = static_cast<FLOAT>(pTouchROIInfo->yMax);
    FLOAT          xMin         = static_cast<FLOAT>(pTouchROIInfo->xMin);
    FLOAT          yMin         = static_cast<FLOAT>(pTouchROIInfo->yMin);
    FLOAT          activeWidth  = static_cast<FLOAT>(m_hardwareInfo.sensorInfo.sensorActiveResWidth);
    FLOAT          activeHeight = static_cast<FLOAT>(m_hardwareInfo.sensorInfo.sensorActiveResHeight);
    FLOAT          resWidth     = static_cast<FLOAT>(m_hardwareInfo.sensorInfo.sensorResWidth);
    FLOAT          resHeight    = static_cast<FLOAT>(m_hardwareInfo.sensorInfo.sensorResHeight);
    StatsRectangle statsROI     = { 0 };
    AECAlgoROI     touchROI;

    GetCropWindow(&statsROI);

    CAMX_LOG_VERBOSE(CamxLogGroupAEC, "input AEREgion: left, top, width, height = %f, %f, %f, %f",
        xMin, yMin, (xMax - xMin), (yMax - yMin));

    // Convert input AE regions to CAMIF size from Sensor Active pixel size
    touchROI.x  = xMin * resWidth / activeWidth;
    touchROI.y  = yMin * resHeight / activeHeight;
    touchROI.dx = (xMax - xMin) * resWidth / activeWidth;
    touchROI.dy = (yMax - yMin) * resHeight / activeHeight;

    // Map AE region to Crop window
    touchROI.x -= statsROI.left;
    touchROI.y -= statsROI.top;

    if (touchROI.x < 0)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "ROI x invalid %d", touchROI.x);
        touchROI.x = 0;
    }

    if (touchROI.y < 0)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "ROI y invalid %d", touchROI.y);
        touchROI.y = 0;
    }

    if ((touchROI.dx + touchROI.x) > statsROI.width)
    {
        touchROI.dx = statsROI.width - touchROI.x;
    }

    if ((touchROI.dy + touchROI.y) > statsROI.height)
    {
        touchROI.dy = statsROI.height - touchROI.y;
    }

    // Calculate ratio of AE region to Crop window and send to Algo
    pHALParam->touchROI.x      = touchROI.x / statsROI.width;
    pHALParam->touchROI.y      = touchROI.y / statsROI.height;
    pHALParam->touchROI.dx     = touchROI.dx / statsROI.width;
    pHALParam->touchROI.dy     = touchROI.dy / statsROI.height;
    pHALParam->touchROI.weight = static_cast<FLOAT>(pTouchROIInfo->weight);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetFaceROISettings
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::SetFaceROISettings(
    AECEngineHALParam*  pHALParam,
    FaceROIInformation* pFaceROIInfo
    ) const
{
    pHALParam->faceROICount = 0;
    for (UINT32 index = 0; index < pFaceROIInfo->ROICount; index++)
    {
        RectangleCoordinate* pRect = &pFaceROIInfo->stabilizedROI[index].faceRect;

        if (pRect->left + pRect->width > m_hardwareInfo.sensorInfo.sensorResWidth ||
            pRect->top + pRect->height > m_hardwareInfo.sensorInfo.sensorResHeight)
        {
            CAMX_LOG_ERROR(CamxLogGroupAEC, "Ignoring invalid ROI SensorDim [%ux%u] < ROI[t:%u l:%u w:%u h:%u]",
                m_hardwareInfo.sensorInfo.sensorResWidth,
                m_hardwareInfo.sensorInfo.sensorResHeight,
                pRect->top,
                pRect->left,
                pRect->width,
                pRect->height);
            continue;
        }

        pHALParam->faceROI[pHALParam->faceROICount].x = static_cast<FLOAT> (pRect->left) /
            static_cast<FLOAT> (m_hardwareInfo.sensorInfo.sensorResWidth);
        pHALParam->faceROI[pHALParam->faceROICount].y = static_cast<FLOAT> (pRect->top) /
            static_cast<FLOAT> (m_hardwareInfo.sensorInfo.sensorResHeight);
        pHALParam->faceROI[pHALParam->faceROICount].dx = static_cast<FLOAT> (pRect->width) /
            static_cast<FLOAT> (m_hardwareInfo.sensorInfo.sensorResWidth);
        pHALParam->faceROI[pHALParam->faceROICount].dy = static_cast<FLOAT> (pRect->height) /
            static_cast<FLOAT> (m_hardwareInfo.sensorInfo.sensorResHeight);

        pHALParam->faceROI[pHALParam->faceROICount].weight = MaxROIWeight;

        pHALParam->faceROICount++;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetBestShotModeSettings
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::SetBestShotModeSettings(
    AECEngineHALParam* pHALParam
    ) const
{
    CamxResult result = CamxResultSuccess;
    UINT32     bestShotmode;

    if (NULL != pHALParam)
    {
        bestShotmode = pHALParam->controlSceneMode;
        switch (bestShotmode)
        {
            case ControlSceneModeAction:
            case ControlSceneModeSteadyphoto:
            case ControlSceneModeSports:
                pHALParam->AEMeteringMode                 = AECAlgoMeteringModeCenterWeighted;          // Center_Weighted
                pHALParam->AECompensation                 = AECompensation0;
                pHALParam->ISOExposureTimePriortyMode     = ISOExposureTimePriorityMode::ISOPriority;
                pHALParam->ISOorExposureTimePriorityValue = static_cast<INT64>(ISOMode::ISOMode400);
                pHALParam->sensitivity                    = AESensitivity_Sports;   // High Gain to achieve Low Exposure time
                break;
            case ControlSceneModeLandscape:
                pHALParam->AEMeteringMode = AECAlgoMeteringModeFrameAverage;                            // Frame_Average
                pHALParam->AECompensation = AECompensation0;
                break;
            case ControlSceneModeBeach:
            case ControlSceneModeSnow:
                pHALParam->AEMeteringMode = AECAlgoMeteringModeFrameAverage;                            // Frame_Average
                pHALParam->AECompensation = AECompensation_Beach_Snow;
                break;
            case ControlSceneModeSunset:
                pHALParam->AEMeteringMode = AECAlgoMeteringModeFrameAverage;                            // Frame_Average
                pHALParam->AECompensation = AECompensation_Sunset_CandleLight;
                break;
            case ControlSceneModeCandlelight:
                pHALParam->AEMeteringMode = AECAlgoMeteringModeCenterWeighted;                          // Center_Weighted
                pHALParam->AECompensation = AECompensation_Sunset_CandleLight;
                break;
            case ControlSceneModePortrait:
            case ControlSceneModeNight:
            case ControlSceneModeNightPortrait:
            case ControlSceneModeTheatre:
            case ControlSceneModeFireworks:
            case ControlSceneModeParty:
                pHALParam->AEMeteringMode = AECAlgoMeteringModeCenterWeighted;                          // Center_Weighted
                pHALParam->AECompensation = AECompensation0;
                break;
            case ControlSceneModeDisabled:
            default:
                break;
        }
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "Scene Mode: %d AE Metering Mode:%d AE Compensation:%d Sensitivity:%d",
                         bestShotmode, pHALParam->AEMeteringMode, pHALParam->AECompensation, pHALParam->sensitivity);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupAEC, "Input HAL Param is NULL");
        result = CamxResultEInvalidPointer;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::GetFlashInfoType
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AECFlashInfoType CAECStatsProcessor::GetFlashInfoType(
    AECAlgoFlashStateType flashState
    ) const
{
    AECFlashInfoType flashInfo = FlashInfoTypeOff;
    switch (flashState)
    {
        case AECAlgoFlashStatePre:
            flashInfo = FlashInfoTypePre;
            break;
        case AECAlgoFlashStateMain:
            flashInfo = FlashInfoTypeMain;
            break;
        case AECAlgoFlashStateCalibaration:
            flashInfo = FlashInfoTypeCalibration;
            break;
        case AECAlgoFlashStateOff:
            flashInfo = FlashInfoTypeOff;
            break;
        default:
            break;
    }
    return flashInfo;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::SetISOExpPriorityValue
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CAECStatsProcessor::SetISOExpPriorityValue(
    AECEngineHALParam* pHALParam
    ) const
{
    ISOExposureTimePriorityMode priorityMode =
                    static_cast<ISOExposureTimePriorityMode>(pHALParam->ISOExposureTimePriortyMode);

    switch (priorityMode)
    {
        case ISOExposureTimePriorityMode::ISOPriority:
            {
                ISOMode isoMode = static_cast<ISOMode>(pHALParam->ISOorExposureTimePriorityValue);

                switch (isoMode)
                {
                    case ISOMode::ISOModeAuto:
                        break;
                    case ISOMode::ISOModeDeblur:
                        break;
                    case ISOMode::ISOMode100:
                        pHALParam->sensitivity = 100;
                        break;
                    case ISOMode::ISOMode200:
                        pHALParam->sensitivity = 200;
                        break;
                    case ISOMode::ISOMode400:
                        pHALParam->sensitivity = 400;
                        break;
                    case ISOMode::ISOMode800:
                        pHALParam->sensitivity = 800;
                        break;
                    case ISOMode::ISOMode1600:
                        pHALParam->sensitivity = 1600;
                        break;
                    case ISOMode::ISOMode3200:
                        pHALParam->sensitivity = 3200;
                        break;
                    case ISOMode::ISOModeAbsolute:
                        pHALParam->sensitivity = pHALParam->ISOValue;
                        break;
                    default:
                        CAMX_LOG_ERROR(CamxLogGroupAEC, "Unsupported ISO mode %d", isoMode);
                        break;
                }
                break;
            }
        case ISOExposureTimePriorityMode::ExposureTimePriority:
            {
                pHALParam->exposureTime = pHALParam->ISOorExposureTimePriorityValue;
                break;
            }
        case ISOExposureTimePriorityMode::DisablePriority:
            break;
        default:
            {
                CAMX_LOG_ERROR(CamxLogGroupAEC, "Unsupported priority mode %d", priorityMode);
                break;
            }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::ReadAFDMode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::ReadAFDMode(
    AECEngineHALParam* pHALParam
    ) const
{
    CamxResult        result                  = CamxResultSuccess;
    AFDFrameInfo*     pAFDFrameInfo           = { 0 };
    static const UINT GetProps[]              = { PropertyIDAFDFrameInfo };
    static const UINT GetPropsLength          = CAMX_ARRAY_SIZE(GetProps);
    VOID*             pData[GetPropsLength]   = { 0 };
    UINT64            offsets[GetPropsLength] = { 1 };

    if (ControlAEAntibandingModeAuto  == pHALParam->AEAntibandingModeValue  ||
        ControlAEAntibandingAuto_50HZ == pHALParam->AEAntibandingModeValue  ||
        ControlAEAntibandingAuto_60HZ == pHALParam->AEAntibandingModeValue)
    {
        m_pNode->GetDataList(GetProps, pData, offsets, GetPropsLength);

        if (NULL != pData[0])
        {
            pAFDFrameInfo = static_cast<AFDFrameInfo*>(pData[0]);

            pHALParam->AEAntibandingModeValue = static_cast<ControlAEAntibandingModeValues>(pAFDFrameInfo->detectedAFDMode);

            CAMX_LOG_VERBOSE(CamxLogGroupAEC, "AFD mode at AEC side %d", pHALParam->AEAntibandingModeValue);
        }
    }
    else
    {
        // Nothing to do here.
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAECStatsProcessor::ReadStatsNodesUpdates
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CAECStatsProcessor::ReadStatsNodesUpdates(
    AECEngineNodesUpdate* pAECNodesUpdate
    ) const
{
    CAMX_ENTRYEXIT(CamxLogGroupAEC);
    CAMX_ASSERT(NULL != pAECNodesUpdate);
    CamxResult  result = CamxResultSuccess;

    static const UINT StatsData[] =
    {
        PropertyIDAFFrameInfo,
        PropertyIDAWBInternal,
        PropertyIDAWBFrameInfo,
        InputControlAWBMode,
        PropertyIDRERCompleted
    };

    static const UINT StatsLength                  = CAMX_ARRAY_SIZE(StatsData);
    VOID*             pData[StatsLength]           = { 0 };
    UINT64            statsDataOffset[StatsLength] = { 1, 1, 1, 1, 1 };

    m_pNode->GetDataList(StatsData, pData, statsDataOffset, StatsLength);

    if (NULL != pData[0])
    {
        AFFrameInformation* pAfFrameInfo = reinterpret_cast<AFFrameInformation*>(pData[0]);
        pAECNodesUpdate->pAFOutput->status.status = pAfFrameInfo->focusStatus.status;
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "AF state received: %d", pAfFrameInfo->focusStatus.status);
    }

    if (NULL != pData[1])
    {
        AWBOutputInternal* pAWBOutputInternal = reinterpret_cast<AWBOutputInternal*>(pData[1]);
        pAECNodesUpdate->pAWBOutputInternal->flashEstimationStatus =
                pAWBOutputInternal->flashEstimationStatus;
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "AWB Flash Estimation state Received: %d",
            pAECNodesUpdate->pAWBOutputInternal->flashEstimationStatus);
    }

    if (NULL != pData[2])
    {
        *pAECNodesUpdate->pAWBFrameInfo = *reinterpret_cast<AWBFrameInfo*>(pData[2]);
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "AWB Frame Info: R,G,B gains: %f, %f, %f",
            pAECNodesUpdate->pAWBFrameInfo->AWBGains.rGain,
            pAECNodesUpdate->pAWBFrameInfo->AWBGains.gGain,
            pAECNodesUpdate->pAWBFrameInfo->AWBGains.bGain);
    }

    if (NULL != pData[3])
    {
        ControlAWBModeValues AWBMode = *reinterpret_cast<ControlAWBModeValues*>(pData[3]);
        pAECNodesUpdate->isAWBModeAuto = (AWBMode == ControlAWBModeAuto);
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "AWB Control Mode: Auto %d",
            pAECNodesUpdate->isAWBModeAuto);
    }

    if (NULL != pData[4])
    {
        pAECNodesUpdate->isRERDone = *reinterpret_cast<BOOL*>(pData[4]);
        CAMX_LOG_VERBOSE(CamxLogGroupAEC, "RER completion Status: %d", pAECNodesUpdate->isRERDone);
    }

    return result;
}
CAMX_NAMESPACE_END
